diff --git a/backup.md b/backup.md new file mode 100644 index 0000000..f6e87a6 --- /dev/null +++ b/backup.md @@ -0,0 +1,255 @@ +# Backup + +## Pregled + +[Dokumentacija](http://meskyanichi.github.io/backup/v3/) + +Backup (gem) pruža uredan DSL za kreiranje backup skripti za arhiviranje fajlova i baza podataka kroz skladišta podataka (Storages) kao što su: + +* Amazon S3 +* Rackspace Cloud Files +* Ninefold +* Dropbox +* FTP +* SFTP +* SCP +* RSync +* Lokalno + +Sva navedena skladišta, osim RSync-a, podržavaju: + +* **Cycling** (da odredimo broj backup fajlova koji će se čuvati. Kada se dostigne limit, najstariji fajl se briše (kružno čuvanje)). +* **Splitter** (da velike pakete rastavimo na manje djelove) + +Backup job-ove kreiramo kao nove modele koristeći Ruby DSL: + + Backup::Model.new(:my_backup, 'Description for my_backup') do + # ... Model Components ... + end + +**:my_backup** je simbol koji se koristi kao trigger i koristi se da se izvrši ***job***. + + $ backup perform --trigger my_backup + +### Arhive i baze podataka + +Arhive kreiraju osnovne **tar** arhive. Baze kreiraju backup-ove za jednu od sljedećih baza podataka koje su podržane: + +* MySQL +* PostgreSQL +* MongoDB +* Redis +* Riak + +Unutar backup modela se može definisati bilo koji broj arhiva i baza podataka. + +### Kompresori i Enkriptori + +Dovanjem kompresora sve arhive i baze podataka će biti kompresovana unutar jedne arhive. Backup uključuje [Gzip](http://meskyanichi.github.io/backup/v3/compressor-gzip/), [Bzip2](http://meskyanichi.github.io/backup/v3/compressor-bzip2/) i [Custom](http://meskyanichi.github.io/backup/v3/compressor-custom/) kompresore. + +### Notifikacije + +Notifiers se koriste da šalju notifikacije nakon uspješnog ili neuspješnog backup modela. Podržani servisi notifikacije su: + +* Email (SMTP, Sendmail, Exim i File delivery) +* Twitter +* Campfire +* Prowl +* Hipchat +* Pushover +* Nagios +* HTTP POST + +Više detalja je dostupno na [ovom](http://meskyanichi.github.io/backup/v3/notifiers/) linku. + +## Instalacija + +U Gemfile-u dodajte gem 'backup'. + + $ bundle install + +Generisanje backup modela: + + $ backup generate:model --trigger ideus_backup \ + --archives --storages='local' --compressors='gzip' --notifiers='mail' + +Komande za generisanje modela: + + $ backup help generate:model + +[Primjeri](http://meskyanichi.github.io/backup/v3/generator/) + +Ovo će generisati novi backup model fajl u ~/Backup/models/ideus_backup.rb koji izgleda ovako: + + Model.new(:ideus_backup, 'Description for ideus_backup') do + + split_into_chunks_of 250 + + archive :my_archive do |archive| + archive.add "/path/to/a/file.rb" + archive.add "/path/to/a/folder/" + archive.exclude "/path/to/a/excluded_file.rb" + archive.exclude "/path/to/a/excluded_folder/" + end + + store_with Local do |local| + local.path = "~/backups/" + local.keep = 5 + end + + compress_with Gzip + + notify_by Mail do |mail| + mail.on_success = true + mail.on_warning = true + mail.on_failure = true + + mail.from = "sender@email.com" + mail.to = "receiver@email.com" + mail.address = "smtp.gmail.com" + mail.port = 587 + mail.domain = "your.host.name" + mail.user_name = "sender@email.com" + mail.password = "my_password" + mail.authentication = "plain" + mail.encryption = :starttls + end + + end + +Takođe se kreira konfiguracioni fajl config.rb. Kada se pokrene backup, prvo se učita ovaj fajl gdje se mogu vršiti opšta podešavanja. + +Za sada preskačemo notifikacije. + +Prilagodićemo ideus_backup.rb za Expense Manager app. + + Backup::Model.new(:expense_manager, 'Backup for expenses') do + + database PostgreSQL do |db| + db.name = "expman_development" + db.username = "expman" + db.password = "secret" + db.host = "localhost" + db.port = 5432 + end + + split_into_chunks_of 250 + + archive :config_archive do |archive| + # :config_archive će biti ime tar fajla + archive.add "config" + archive.exclude "config/deploy" + # osim foldera, mogu se dodavati i isključivati fajlovi. + end + + store_with Local do |local| + local.path = "~/backups/" + local.keep = 5 + end + + compress_with Gzip + + end + +Da provjerimo da li postoji neka sintaksna greška: + + $ backup check + +### Izvršavanje backup-a + + $ backup perform --triger expense_manager + +### Rake task za povratak baze + + namespace :db do + task import: :environment do + import_path = "~/backups" + sql_file = "PostgreSQL.sql" + # za sada, sami raspakujte ovaj fajl + database_config = Rails.configuration.database_configuration[Rails.env] + + # Unpack + # system "tar xf ides_backup.tar -C #{import_path}" + # system "gzip -df #{import_path}/#{sql_file}.gz" + + # Import + system "psql --username=#{database_config['username']} -h localhost #{database_config['database']} < #{import_path}/#{sql_file}" + end + end + +## Whenever gem + +Whenever je Ruby gem koji omogućava pisanje i izvršavanja cron job-ova (koji se koriste za automatsko izvšavanje taskova u određenim vremenskim periodima). + +### Installacija + +[Dokumentacija](https://github.com/javan/whenever) + + $ gem install whenever + +ili sa Bundlerom u Gemfile-u: + + gem 'whenever', :require => false + +Unutar projekta: + + $ cd /apps/my-project + $ wheneverize . + +Ovo će kreirati config/schedule.rb fajl. + +Primjeri: + + every 3.hours do + runner "MyModel.some_process" + rake "my:rake:task" + command "/usr/bin/my_great_command" + end + + every 1.day, :at => '4:30 am' do + runner "MyModel.task_to_run_at_four_thirty_in_the_morning" + end + + every :hour do # Dostupno: :hour, :day, :month, :year, :reboot + runner "SomeModel.ladeeda" + end + + every :sunday, :at => '12pm' do # Ili bilo koji dan u sedmici ili :weekend, :weekday + runner "Task.do_something_great" + end + + every '0 0 27-31 * *' do + command "echo 'you can use raw cron syntax too'" + end + + # pokreni ovaj taks samo na serverima sa :app role u Capistrano-u + # pogledaj Capistrano roles sekciju u dokmentaciji + every :day, :at => '12:20am', :roles => [:app] do + rake "app_server:task" + end + +Cron syntax: http://www.nncron.ru/help/EN/working/cron-format.htm + +Lista svih opcija se može vidjeti pomoću: + + $ whenever --help + +### Backup + Whenever + +Nakon instalacije whenever gem, kreiraj folder config. + + $ gem install whenever + $ cd ~/Backup + $ mkdir config + $ wheneverize . + +U config/schedule.rb: + + every 1.minute do + command "cd ~/rails_projects/expman && backup perform --trigger expense_manager" + end + +Na kraju u Backup folderu, pokrećemo cron job da radi svaki minut: + + $ whenever + $ whenever --update-crontab \ No newline at end of file diff --git a/i18n.md b/i18n.md new file mode 100644 index 0000000..6e79780 --- /dev/null +++ b/i18n.md @@ -0,0 +1,598 @@ +# Rails Internationalization (I18n) API + +Ruby-jev I18n gem pruža jednostavan i proširiv framework za **prevođenje** aplikacije na jedan ili više jezika. U procesu **internacionalizacije** (izdvajanja svih stringova iz aplikacije) moramo obezbijediti podršku za i18n, reći Railsu gdje se nalaze lokalni rječnici (locale dictionaries) i reći Railsu kako da podesi, sačuva i promijeni 'locale'-e. U procesu **lokalizacije** zamjenjujemo ili dopunjujemo Rails default locale - npr. format za datum i vrijeme, imena mjeseci, imena Active Record mdoela itd. Izdvajamo stringove u aplikaciji u ključne rječnike, kao flash poruke, tekst iz view-ova itd. Na kraju, negdje čuvamo rezultujuće rječnike. + +# 1 Kako I18n radi u ROR-u + +Prirodni jezici se razlikuju na mnogo načina (npr. u pravilima množine) tako da je teško napraviti alate koji rješavaju sve probleme odjednom. Iz tog razloga Rails I18n API se fokusira na sljedeće: + +* pružanje podrške za engleski jezik i slične jezike +* omogućavanje lakog prilagođavanja ili proširenja svega za druge jezike + +Svaki statički string u Rails frameworku (npr. Active Record validacione poruke, format za vrijeme i datum) su internacionalizovani tako da lokalizacija znači over-ridovanje ovih defaulta. + +## 1.1 Arhitektura biblioteka + +Iako je Ruby I18n gem podijeljen na dva dijela: + +* Javni API i18n framework - Ruby modul sa javnim metodama koje definišu kako biblioteka funkcioniše +* Default backend (koji je nazvan *Simple* backend) koji implementira ove metode. + +kao korisnik možete pristupiti samo javnim metodama I18n modula, ali je korisno znati mogućnosti backenda. + +## 1.2 Javni I18n API + +Najvažnije metode I18n API-ja su: + + translate # pronalazak prevoda teksta + localize # lokalizacija objekata Date and Time na lokalni format + +Možemo ih koristiti u skraćenoj formi: + + I18n.t 'store.title' + I18n.l Time.now + +Postoje i attribute readers & writers za sljedeće atribute: + + load_path # Govori gdje se nalaze fajlovi prevoda + locale # Dohvata i podešava trenutni locale + default_locale # Dohvata i podešava default locale + exception_handler # Korišćenje drugačijeg exception_handlera + backend # Korišćenje drugačijeg backenda + +# 2 Podešavanja Rails aplikacija za internacionalizaciju + +## 2.1 Konfiguracija I18n modula + +Rails ima već default podešavanja jezika u aplikaciji. Ako su potrebna drugačija podešavanja, mogu se lako prepisati. Rails dodaje sve .rb i .yml fajlove u config/locales folderu u **translation load path**, automatski. + +Default en.yml fajl sadrži prosti par stringova: + + en: + hello: "Hello world" + +Ovo znači da u locale-u :en, ključ *hello* će mapirati *Hello world* string. Svaki string u Railsu je internacionalizovan na ovaj način. Pogledajmo na primjer, Active Model validacione poruke u [https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml](activemodel/lib/active_model/locale/en.yml) fajlu ili formate za vrijeme i datum u [https://github.com/rails/rails/blob/master/activesupport/lib/active_support/locale/en.yml](activesupport/lib/active_support/locale/en.yml). Možete koristiti YAML ili standardne Ruby hasheve da sačuvate prevode u default (Simple) backend. + +I18 biblioteka će koristiti engleski kao default locale. Npr. ako ne podesite drugi locale, :en će se koristiti za pretragu prevoda. + +Default application.rb fajl sadrži instrukcije kako se dodaju locale-i iz drugog direktorijuma i kako podesiti drugi locale. + + # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. + # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] + # config.i18n.default_locale = :de + +## 2.2 Custom i18n konfiguracija + + # in config/initializers/locale.rb + + # tell the I18n library where to find your translations + I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')] + + # set default locale to something other than :en + I18n.default_locale = :pt + +## 2.3 Podešavanje i proslijeđivanje locale-a + +Ako želite da prevedete aplikaciju na neki jezik (koji nije engleski) i da to bude jedini jezik u aplikaciji, možete podesiti I18n.default_locale kao locale u application.rb. Međutim, najčešće je slučaj korišćenja više jezika u aplikaciji, tako da se locale-i moraju podesiti i proslijediti između zahtjeva. + +Setovanje je lako. Može se koristi before_action u ApplicationControlleru: + + before_action :set_locale + + def set_locale + I18n.locale = params[:locale] || I18n.default_locale + end + +Ovo zahtijeva prosleđivanje jezika kao URL query parametra kao http://localhost:3000?locale=pt (učitaće portugalsku lokalizaciju). Ali, ako ne želimo da na ovaj način učitavamo locale na čitavome sajtu ili imamo različite URL adrese za različite lokalizacije poput http://example.com/pt/books i http://example.com/en/books možemo koristiti druge opcije. O tome će biti riječi u nastavku. + +## 2.4 Postavljanje locale-a iz domena + +Jedna od opcija je postaviti locale iz domena gdje se aplikacije pokreće. Na primjer, ako želimo da www.example.com učitava engleski (ili default) locale, a da www.example.es učita španski locale. + +Ovo se može implementirati na sljedeći način: + + before_action :set_locale + + def set_locale + I18n.locale = extract_locale_from_tld || I18n.default_locale + end + + # Get locale from top-level domain or return nil if such locale is not available + # You have to put something like: + # 127.0.0.1 application.com + # 127.0.0.1 application.it + # 127.0.0.1 application.pl + # in your /etc/hosts file to try this out locally + def extract_locale_from_tld + parsed_locale = request.host.split('.').last + I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil + end + +Možemo podesiti locale i iz poddomena na sličan način: + + # Get locale code from request subdomain (like http://it.application.local:3000) + # You have to put something like: + # 127.0.0.1 gr.application.local + # in your /etc/hosts file to try this out locally + def extract_locale_from_subdomain + parsed_locale = request.subdomains.first + I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil + end + +Ako stranica uključuje meni za promjenu locale-a, potrebno nam je nešto ovakvo: + + link_to("Deutsch", "#{APP_CONFIG[:deutsch_website_url]}#{request.env['REQUEST_URI']}") + +pod uslovom da APP_CONFIG[:deutsch_website_url] ima vrijednost poput http://www.application.de. + +## 2.5 Postavljanje locale-a iz URL Params + +Najčešći način postavljanja i prosljeđivanja locale-a bio bi dodavanje istog, URL paramsima, kao u prvom primjeru sa I18n.locale = params[:locale] i before_action. Želimo da imamo URL adrese poput **www.example.com/books?locale=ja** ili **www.example.com/ja/books**. + +Implementacija prethodnog je nešto složenija od načina predstavljenog u prethodnom poglavlju. + +Dohvatanje locale-a iz paramsa i postavljanje istog nije teško; uključiti ga u svaki URL i proslijediti kroz zahtjev, jeste. Da uključimo locale u svaki url, opcijom poput link_to(books_url(locale: I18n.locale)) je praktično nemoguće. + +Rails sadrži infrastrukturu za "centralizing dynamic decisions about the URLs" u ApplicationController#default_url_options, što je korisno u ovom scenariju: omogućava postavljanje defaulta za **url_for** i helper metode vezane za isto (implementiranjem ili prepisivanjem ove metode). + +U ApplicationControlleru možete uključiti sljedeće: + + # app/controllers/application_controller.rb + def default_url_options(options={}) + logger.debug "default_url_options is passed options: #{options.inspect}\n" + { locale: I18n.locale } + end + +Svaki helper metod zavisan od url_for (kao npr. helperi za imenovanje ruta root_path ili root_url, resouce rote kao books_path ili books_url itd.) će sada automatski uključiti local u query string, kao: http://localhost:3001/?locale=ja. + +Ovo može biti zadovoljajuće izuzimajući uticaj na čitljivost URL-a. + +Ako želimo da URL izgledao kao www.example.com/en/books (koji će učitati engleski locale), onda vršimo over-riding default_url_options strategiju spomenutu iznad. Jedina izmjena je u rutama gdje definišemo scope na sljedeći način: + + # config/routes.rb + scope "/:locale" do + resources :books + end + +Sada, kada pozovete books_path dobijate "/en/books". + +Ako ne želite da navođenje jezika u URL-u bude obavezno, možete koristit opcionalni path scope (označen sa zagradama), kao: + + # config/routes.rb + scope "(:locale)", locale: /en|nl/ do + resources :books + end + +Sada nećemo dobijati Routing Error ako pokušavamo da pristupimo http://localhost:3001/books bez navođenja locale-a. Ovo je korisno ako želite da koristite default locale kada nijedan nije specificiran. Ali, treba voditi računa o root URL-u. URL poput http://localhost:3001/nl neće raditi automatski zbog toga što root to: "books#index" u routes.rb ne uzima locale u obzir. + +Biće vjerovatno potrebno mapirati URL adrese kao: + + # config/routes.rb + get '/:locale' => 'dashboard#index' + + +Postoje tri gema koja olakšavaju rad sa rutama na ova način: + +* https://github.com/svenfuchs/routing-filter/tree/master +* https://github.com/enriclluelles/route_translator +* https://github.com/francesc/rails-translate-routes + +## 2.6 Postavljanje locale-a na osnovu client-supplied info + +U nekim slučajevima korisno je podesiti locale iz client-supplied informacije, a ne iz URL-a. Ova informacija može dolazi npr. od jezika koji preferira korisnik (podešenog u browseru), ili na osnovu geografske lokacije dobijene na osnovu IP adrese ili na osnovu korisnikovog izbora jezika na interfejsu aplikacije. Ovaj pristup je pogodniji za web aplikacije ili servise, više nego za web sajtove. + +### 2.6.1 Korišćenje Accept - Language + +Jedan izvor client-supplied informacija je Accept-Language HTTP header. Postoji mogućnost [podešavanja prethodnog u browseru](http://www.w3.org/International/questions/qa-lang-priorities) ili drugim klientima (kao curl). + +Trivijalna implementacija korišćenja Accept-Language headera bi bila: + + def set_locale + logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}" + I18n.locale = extract_locale_from_accept_language_header + logger.debug "* Locale set to '#{I18n.locale}'" + end + + private + def extract_locale_from_accept_language_header + request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first + end + +Naravno, u production okruženju bi bio potreban složeniji kod i mogao bi koristiti plugin kao [http_accept_language](https://github.com/iain/http_accept_language/tree/master) ili Rack middleware [locale](https://github.com/rack/rack-contrib/blob/master/lib/rack/contrib/locale.rb). + +### 2.6.2 Korišćenje GeoIP (ili slične) baze podataka + +Još jedan način korišćenja locale-a iz informacije klijenta bi bio korišćenjem baze podatak za mapiranje IP adrese klienta, kao [GeoIP Lite Country](http://dev.maxmind.com/geoip/legacy/geolite/). Vrši se upit sa IP adresom korisnika nad bazom podataka i koristi se locale na osnovu države, regiona ili grada koji je dobijen kao rezultat. + +### 2.6.3 Korisnički profil + +Još jedna mogućnost je omogućiti korisnicima da unesu preferirani jezik preko interfejsa aplikacije. Korisnik bira local iz dropdown liste i čuva ga u bazi. Onda se jednostavno podesi locale na osnovu ove vrijednosti. + +# 3 Internacionalizacija aplikacije + +U nekoj našoj aplikaciji, sljedeće vjerovatno već imamo: + + # config/routes.rb + Yourapp::Application.routes.draw do + root to: "home#index" + end + + # app/controllers/application_controller.rb + class ApplicationController < ActionController::Base + before_action :set_locale + + def set_locale + I18n.locale = params[:locale] || I18n.default_locale + end + end + + # app/controllers/home_controller.rb + class HomeController < ApplicationController + def index + flash[:notice] = "Hello Flash" + end + end + + # app/views/home/index.html.erb +

Hello World

+

<%= flash[:notice] %>

+ +## 3.1 Dodavanje prevoda + +Vidimo dva stringa na engleskom jeziku. Da bismo internacionalizovali kod, zamjenjujemo ove stringova sa pozivima Rails #t helpera sa ključem koji bi trebao asocirati na stvarni prevod: + + # app/controllers/home_controller.rb + class HomeController < ApplicationController + def index + flash[:notice] = t(:hello_flash) + end + end + + # app/views/home/index.html.erb +

<%=t :hello_world %>

+

<%= flash[:notice] %>

+ +Sada ćemo na index stranici vidjeti poruke o greškama koje govore da nedostaju prevodi za ključeve :hello_world i :hello_flash. Inače, t je zamjena za I18n.t, a uz, helper štampa greške u slučaju da nedostaje prevod. + +Dakle, trebamo dodati vrijednosti za navedene ključeve. (možemo koristiti .yml i .rb fajlove) + + # config/locales/en.yml + en: + hello_world: "Hello world!" + hello_flash: "Hello flash!" + + # config/locales/me.yml + me: + hello_world: "Zdravo svijete!" + hello_flash: "Vozdra flash!" + +**NAPOMENA**: Obratite pažnju na indentaciju! + +Sada localhost:3000 prikazuje vrijednosti ključeva na engleskom jeziku (jer je to default jezik). A kada pokušamo pristupiti localhost:3000/?locale=me dobićemo i prevod na crnogorskom jeziku (a i ostalim ex-Yu jezicima). Da bi se učitao fajl me.yml moramo restartovati server. + +## 3.2 Prosljeđivanje promjenljivih prevodima + +Možete koristiti promjenljive nakon navođenja ključa prevoda i proslijediti ih iz view-a. + + # app/views/home/index.html.erb + <%=t 'greet_username', user: "Bill", message: "Goodbye" %> + + # config/locales/en.yml + en: + greet_username: "%{message}, %{user}!" + +## 3.3 Dodavanje Date/Time formata + +Dodajmo sada timestamp view-u, da demonstriramo svojstvo date/time lokalizacije. Da lokalizujemo format vremena proslijedite Time objekat I18n.l ili koristite Rails #l helper. Format se može pokupiti prosljeđivanjem :format opcije. + + # app/views/home/index.html.erb +

<%=t :hello_world %>

+

<%= flash[:notice] %>

<%= l Time.now, format: :short %>

+ +I u našem me.yml fajlu dodajmo format vremena: + + # config/locales/me.yml + pirate: + time: + formats: + short: "Tu neđe oko %Hh." + +Naravno, veliki dio posla je već neko odradio na [ovom linku](https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/bs.yml). + +Ostali jezici: +https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale + +## 3.4 Inflection pravila + +Rails 4 omogućava definisanje inflection pravila (kao pravila za jedninu i množinu). U config/initializers/inflections.rb možete definisati ova pravila za sve jezike koje koristite u aplikacije. + + ActiveSupport::Inflector.inflections(:me) do |inflect| + #inflect.acronym 'RESTful' + inflect.irregular 'greška', 'greške' + end + +## 3.5 Lokalizovani view-ovi + +Rails 2.3 je predstavio još jedno svojstvo lokalizacije: lokalizovani view-ovi (template-i). Ako npr. imamo BooksController u aplikacije, index akcija renderuje sadržaj u app/views/books/index.html.erb template. Ako u ovom folderu stavimo i lokalizovanu varijantu ovog template-a, index.me.html.erb, Rails će renderovati sadržaj ovdje, kada je locale podešen na :me. Kada je locale podešen na default locale, koristiće se index.html.erb (Naredne verzije Railsa ovu automatsku lokalizaciju stavljaju u assetsima u public). + +## 3.6 Organizacija locale fajlova + +Kada koristite defaultni SimpleStore isporučen sa i18n bibliotekom, rječnici se čuvaju kao plain-text fajlovi na disku. Dodavanje prevoda za sve djelove aplikacije u jednom fajlu po locale-u bi mogao biti težak posao. Ovi fajlovi se mogu čuvati hijerarhijski (kako nama ima smisla). + +Na primjer, config/locales bi mogao izgledati ovako: + + |-defaults + |---me.rb + |---en.rb + |-models + |---book + |-----me.rb + |-----en.rb + |-views + |---defaults + |-----me.rb + |-----en.rb + |---books + |-----me.rb + |-----en.rb + |---users + |-----me.rb + |-----en.rb + |---navigation + |-----me.rb + |-----en.rb + + # config/application.rb + config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')] + +ili + + config.i18n.load_path += Dir["#{Rails.root.to_s}/config/locales/**/*.{rb,yml}"] + +# 4 Pregled I18n API svojstava + +## 4.1 Traženje (Lookup) prevoda + +### 4.1.1 Osnovni lookup, scope-ovi i ugnježdeni ključevi + +Prevodi su traženi po ključevima koji mogu biti u formi simbola ili stringova, tako da je sljedeće ekvivalentno: + + I18n.t :message + I18n.t 'message' + +`translate` metoda takođe uzima `:scope` opciju koja može sadržati jedan ili više ključeva koji će biti korišćeni da specificiraju "namespace" ili scope za tanslation ključ: + + I18n.t :record_invalid, scope: [:activerecord, :errors, :messages] + +Ovo će tražiti `:record_invalid` poruku u Active Record poruke za grešku. + +Naredno je ekvivalentno prethodnom: + + I18n.t 'activerecord.errors.messages.record_invalid' + I18n.t 'errors.messages.record_invalid', scope: :active_record + I18n.t :record_invalid, scope: 'activerecord.errors.messages' + I18n.t :record_invalid, scope: [:activerecord, :errors, :messages] + +### 4.1.2 Defaults + +Kada je data `:default` opcija data, njena vrijednost će biti vraćena ako nedostaje prevod: + + I18n.t :missing, default: 'Not here' + # => 'Not here' + +Ako je `:default` vrijednost simbol, koristiće se kao ključ i biće prevedena. Može biti dodijeljeno više vrijednosti kao default. Prva koja bude u rezultatu će bit vraćena kao konačni rezultat. Npr. sljedeće prvo pokušava da prevede ključ `:missing` a onda i ključ `:also_missing`. Ako nijedan ne vrati rezultat, string "Not here" biće vraćen. + + I18n.t :missing, default: [:also_missing, 'Not here'] + # => 'Not here' + +### 4.1.3 Bulk i Namespace Lookup + +Da bi zahtijevali više prevoda odjednom, niz ključeva se prosljeđuje: + + I18n.t [:odd, :even], scope: 'errors.messages' + # => ["must be odd", "must be even"] + +Takođe, ključ može biti preveden u (možda i ugnježden) hash grupisanih prevoda. Npr. neko može primiti sve Active Record poruke greške kao Hash sa: + + I18n.t 'activerecord.errors.messages' + # => {:inclusion=>"is not included in the list", :exclusion=> ... } + +### 4.1.4 "Lazy" Lookup + +Rails implementira i dobar način za 'look up' jezika unutar view-a. Kada imate sljedeći rječnik: + + me: + books: + index: + title: "Naslov" + +možete pretražiti vrijednost `books.index.title` unutar `app/views/books/index.html.erb` template-a kao (primijetite tačku): + + <%= t '.title' %> + +## 4.2 Interpolacija + +U prevodima će često biti potrebna interpolacija. + + I18n.backend.store_translations :en, thanks: 'Thanks %{name}!' + I18n.translate :thanks, name: 'Jeremy' + # => 'Thanks Jeremy!' + +Ako prevod koristi `:default` i `:scope` kao promjenljive interpolacija, `I18n::ReservedInterpolationKey` će biti vraćen. Ako prevod koristi interpolaciju a ne preda se nijedna promjenljiva, `I18n::MissingInterpolationArgument` će biti vraćen. + +## 4.3 Pluralizacija + +U engleskom jeziku postoji samo jedna forma za jedninu i množinu za dati string, npr. "1 message" i "2 messages". Drugi jezici imaju drugačije gramatike koje imaju dodatne forme množine. + +`:count` promjenljiva interpolacije ima posebnu ulogu jer osim interpolacije se koristi i za pluralizaciju iz prevoda na osnovu pravila množine definisanih u CLDR: + + I18n.backend.store_translations :en, inbox: { + one: 'one message', + other: '%{count} messages' + } + I18n.translate :inbox, count: 2 + # => '2 messages' + + I18n.translate :inbox, count: 1 + # => 'one message' + +Algoritam pluralizacije u `:en` je vrlo prost: + + entry[count == 1 ? 0 : 1] + +Npr. prevod označen sa 1 se označava sa jedninom, inače je množina (uključujući i nulu). + +Ako se ne nađe nije Hash za dati ključ pogodan za pluralizaciju, vraća se 18n::InvalidPluralizationData izuzetak. + +## 4.4 Podešavanje i prosljeđivanje jezika (locale) + +Locale može biti podešen pseudo-globalno na I18n.locale (koja se koristi kao `Thread.current`, npr. Time.zone) ili može biti proslijeđen kao opcija za #translate i #localize. + +Ako nije proslijeđen locale, koristi se I18n.locale: + + I18n.locale = :de + I18n.t :foo + I18n.l Time.now + +Eksplicitno prosljeđivanje locale-a: + + I18n.t :foo, locale: :de + I18n.l Time.now, locale: :de + +Default locale može biti podešen kao: + + I18n.default_locale = :de + +## 4.5 Korišćenje HTML sigurnih prevoda + +Ključevi sa '_html' sufiksom i ključevi imenovani sa 'html' su označeni kao HTML sigurni. Kada ih koristite u view-ovima, HTML će biti primijenjen: + + # config/locales/en.yml + en: + welcome: welcome! + hello_html: hello! + title: + html: title! + + # app/views/home/index.html.erb +
<%= t('welcome') %>
+
<%= raw t('welcome') %>
+
<%= t('hello_html') %>
+
<%= t('title.html') %>
+ +Output: + +welcome! +**welcome!** +**hello!** +**title!** + +# 5 Kako čuvati vaše custom prevode + +Prevode je moguće čuvati u plain Ruby i Yaml formatima. + +Npr. Ruby Hash koji daje prevode može izgledati ovako: + + { + pt: { + foo: { + bar: "baz" + } + } + } + +Ekvivalentni YAML fajl bi izgledao ovako: + + pt: + foo: + bar: baz + +U oba slučaja, na najvišem nivou je ključ za jezik. :foo je namespace ključ a :bar je ključ za prevod "baz". + +Evo realnog primjera iz Active Support en.yml prevoda YAML fajla: + + en: + date: + formats: + default: "%Y-%m-%d" + short: "%b %d" + long: "%B %d, %Y" + +Svi sljedeći lookup-ovi će vratiti :short format datuma "%b %d": + + I18n.t 'date.formats.short' + I18n.t 'formats.short', scope: :date + I18n.t :short, scope: 'date.formats' + I18n.t :short, scope: [:date, :formats] + +Preporučuje se korišćenje YAML formata za čuvanje prevoda. + +# 5.1 Prevodi za Active Record modele + +Možete koristiti modele `Model.model_name.human` i `Model.human_attribute_name(attribute)` da transparentno pretražite prevode za vaš model i imena atributa. + +Na primjer, možete dodati sljedeće prevode: + + en: + activerecord: + models: + user: Dude + attributes: + user: + login: "Handle" + # will translate User attribute "login" as "Handle" + +`User.model_name.human` će vratiti "Dude" i `User.human_attribute_name("login")` će vratiti "Handle". + +Takođe se može podesiti forma za množinu za imene modela, dodavajući sljedeće: + + en: + activerecord: + models: + user: + one: Dude + other: Dudes + +`User.model_name.human(count: 2)` će vratiti "Dudes". Sa `count: 1` ili bez parametara će vratiti "Dude". + +Active Record porukama za greške validacije je takođe moguđe lako upravljati. + +## 5.3 Ostale ugrađene I18n metode + +`distance_of_time_in_words` prevodi i formira množinu, vrši interpolaciju za broj sekundi, minuta, sati itd. + +`datetime_select and select_month` koristi prevedena imena mjeseci za popunjavanje rezultujućeg select taga. + +`number_to_currency`, `number_with_precision`, `number_to_percentage`, `number_with_delimiter` i `number_to_human_size` helperi koriste podešavanja formata broja lociranih unutar number scope-a. + +`model_name.human` i `human_attribute_name` koristi prevode za imena modela i imena atributa ako postoji u activerecord.models scope-u. + +`ActiveModel::Errors#generate_message` (koji je korišćen od Active Record validacije ali se može i ručno koristiti) koristi `model_name.human` i `human_attribute_name`. Takođe prevodi poruke greške i podržava prevode za naslijeđena imena klase. + +`ActiveModel::Errors#full_messages` nadovezuje ime atributa na poruku greške koristeći separator koji će se uzeti iz errors.format ("%{attribute} %{message}"). + +# 6 Izmjena I18n podešavanja + +## 6.1 Korišćenje različitih backenda + +Iz nekoliko razloga Simple backend radi samo "najprostiju stvar koja bi mogla raditi" za RoR što znači da se garantuje da radi za engleski jezik i eventualno jezike koji su VRLO slični engleskom. Takođe, simple backend je sposoban da čita prevode ali ne i da ih dinamički čuva u bilo koji format. + +That does not mean you're stuck with these limitations, though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs. E.g. you could exchange it with Globalize's Static backend: + + I18n.backend = Globalize::Backend::Static.new + +Moguće je koristiti i Chain backend za pravljenje lanca više backenda. Ovo je korisno kada koristimo standardne prevode sa Simple backendom ali čuvamo custom prevode aplikacije u bazi ili druge backende. Npr. možemo koristiti Active Record backend sa Simple backendom: + + I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend) + +Moguće i upravljati Exeption Handlerima i koristiti neke druge od onih standardnih. + +I18n API definiše sljedeće izuzetke: + + MissingTranslationData # no translation was found for the requested key + InvalidLocale # the locale set to I18n.locale is invalid (e.g. nil) + InvalidPluralizationData # a count option was passed but the translation data is not suitable for pluralization + MissingInterpolationArgument # the translation expects an interpolation argument that has not been passed + ReservedInterpolationKey # the translation contains a reserved interpolation variable name (i.e. one of: scope, default) + UnknownFileType # the backend does not know how to handle a file type that was added to I18n.load_path \ No newline at end of file diff --git a/jenkins.md b/jenkins.md new file mode 100644 index 0000000..e6d5fb7 --- /dev/null +++ b/jenkins.md @@ -0,0 +1,203 @@ +# Jenkins + +Jenkins je open source [continous integration](https://en.wikipedia.org/wiki/Continuous_integration) Java alat. Jenkins pruža CI servise za razvoj softvera. + +## Instalacija + +Dokumentacija: + +* [Link 1](http://blog.loadimpact.com/2013/11/29/bootstrap-your-ci-with-jenkins-and-github) +* [Link 2](http://dean.io/setting-up-jenkins-ci-for-ruby-on-rails-testing/) +* [Link 3](http://blogs.burnsidedigital.com/2013/01/setting-jenkins-ci-server-for-rails-project-on-a-vagrant-box/) + + +Dodavanje ključa repozitorijuma: + + $ wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - + +Rješenje za mogući problem ***gpg: no valid opengpg data found occurs***, potražite [ovdje](http://stackoverflow.com/questions/18967942/gpg-import-fails-with-no-valid-openpgp-data-found) ili [ovdje](http://stackoverflow.com/questions/21338721/gpg-no-valid-openpgp-data-found). + +Sada treba dodati repozitorijum, source listi: + + $ /etc/apt/sources.list + $ deb http://pkg.jenkins-ci.org/debian binary/ + +Ako na serveru, ova dva koraka ne rade, onda: + + $ sudo -i + echo 'deb http://pkg.jenkins-ci.org/debian binary/' >> /etc/apt/sources.list + exit + +ili: + + $ echo 'deb http://pkg.jenkins-ci.org/debian binary/' | sudo tee -a /etc/apt/sources.list + +Sada treba update-ovati listu paketa: + + $ sudo apt-get update + +i instalirati Jenkins: + + $ sudo apt-get install jenkins + +Sada bi Jenkins trebao biti dostupan na localhost:8080 ili IP:8080 na nekom serveru. + +Na serveru će nam biti potrebno da Jenkins bude dostupan na portu 80 kako bismo na kraju ispravno povezali GitHub i Jenkins. + +Instalirajmo najprije nginx: + + $ sudo aptitude -y install nginx + +Izbišrimo default podešavanja u sites-enables: + + $ cd /etc/nginx/sites-available + $ sudo rm default ../sites-enabled/default + +U sites-available kreiramo jenkins file: + + upstream app_server { + server 127.0.0.1:8080 fail_timeout=0; + } + + server { + listen 80; + listen [::]:80 default ipv6only=on; + server_name ci.yourcompany.com; //(instead the server_name, add your IP - remove this comment!) + + location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + + if (!-f $request_filename) { + proxy_pass http://app_server; + break; + } + } + } + +Sada linkujemo ovaj konfiguracioni fajl iz site-available u sites-enable: + + $ sudo ln -s /etc/nginx/sites-available/jenkins /etc/nginx/sites-enabled/ + $ sudo service nginx restart + +Sada bi jenkins trebao biti dostupan na IP:80 (ili samo IP) a da se omogući pristup jenkinsu sa IP/jenkins umjesto location / u jenkins configuracionom fajlu, pišemo: location ^~ /jenkins. + +Ako naiđete na problem ***It appears that your reverse proxy setup is broken***, probajte sljedeće: + +* [link1](http://www.phase2technology.com/blog/running-jenkins-behind-nginx/) +* [link2](https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+says+my+reverse+proxy+setup+is+broken) + +ili u jenkins conf fajlu dodajte: + + proxy_pass http://localhost:8080; + proxy_read_timeout 90; + proxy_redirect http://localhost:8080 $scheme://(IP or domain); + + $ service nginx restart + $ service jenkins restart + +## Instalacija plugin-ova + +Pluginovim se može pristupiti unutar Manage Jenkins u side baru, zatim Manage plugins pod karticom Available plugins. + +Instalirajte sljedeće pluginove: + +* Git +* GitHub +* Rake +* RVM +* Ruby +* Ruby metrics +* rbenv +* Green ball +* ANSI color + +Kliknite na **install without restart** i čekirajte ***restart after installation*** ispod instalacije pluginova. + +## Sigurnost + +* U Jenkins > Manage Jenkins > Configure Global Security čekirajte ***Enable security***. + * Pod **Security Realm**, odaberite ***Jenkins's own user database*** i čekirajte ***Allow users to sign up*** + * Pod **Authorization**, odaberite ***Project-based Matrix Authorization Strategy***. S obzirom da korisničko ime za administratorski pristup mora biti **admin**, dodajte prvog korisnika sa ***admin*** i čekirajte sve dozvole. Dodajte drugog korisnika sa ***github*** i čekirajte samo **Read** čekboks u Overall koloni. + +**Napomena**: Prethodni koraci ne kreiraju admin i github korisnike tako da ih moramo kreirati ručno. Klikom na Save, pojaviće se prozor za login. + +## Dodavanje SSH ključa + +Ponovite korake za dodavanje SSH ključa sa [GitHub help stranice](https://help.github.com/articles/generating-ssh-keys) ali promijenite ime fajla da ne bi izbrisali stari ključ (npr. id_rsa2). +Dodajte ključ na GitHubu na ImeProjekta > Settings > Deploy key + +Isti ključ dodajemo i na jenkinsu: + +Jenkins > Credentials > Global Credentials > Add Credentials +**Kind**: SSH Username with private key + +Sada možemo kreirati prvi projekat. Na glavnoj stranici biramo opciju New Item. Dodajte neko ime i odaberite **Build a free software project**. + +Na sljedećoj stranici dodajte link GitHub projekta. Npr: + + https://github.com/enter08/Expense-Manager/ + +Pod Source Code Management odaberite Git i dodajte link repozitorijuma. Npr: + + https://github.com/enter08/Expense-Manager.git + +Iz drop down liste Credentials, odaberite nedavno kreirani ključ. + +Da bi trigger **Build when a change is pushed to GitHub** funkcionisao, pročitajte sljedeće (ne može lokalno): +* [Trigger jenkins builds](http://fourkitchens.com/blog/2011/09/20/trigger-jenkins-builds-pushing-github) +* [How to configure git post commit hook](http://stackoverflow.com/questions/12794568/how-to-configure-git-post-commit-hook) + +Da testirate do sada urađeno, napravite neku izmjenu na projektu i push-ujte to na GitHub. Sačuvajte sve u konfiguracijama jenkinsa i kliknite na **Build now**. + +U Build history ćete vidjeti broj job-a i datum. Klikom na link dobićemo rezultate build-a. Na trenutnoj stranici bi trebalo da piše koja promjena je napravljena na projektu. Inače, u Build history svaki uspješni build je označen zelenom loptom, neuspješni crvenom. + +Vratimo se podešavanjima. Kliknite na **Configure**. + +Pod **Build Environment** čekirajte **rbenv build wrapper**. +Unesite i svoju ruby verziju. Npr. 2.0.0-p353. + +U database.yml.example dodajte informacije testne baze podataka (kopirajte iz database.yml). + +Pod Build > Execute Shell, dodajte sljedeće linije: + + cp config/database.yml.example config/database.yml + bundle install + rake db:create + rake db:schema:load + rake db:test:prepare + rake ci:setup:rspec spec + +Da bi ci:setup:rspec (continous integration) radio, potreban nam je ci_reporter gem. +Dodajte gem '[ci_reporter](https://github.com/nicksieger/ci_reporter)', '1.8.0' unutar test grupe. +(**Napomena**: Navedite tačnu verziju, jer posljednja verzija 1.8.3, ne radi ispravno). + +U Rakefile-u dodajte: + + require 'ci/reporter/rake/rspec' + require 'ci/reporter/rake/cucumber' + +Trebaće nam i simple_cov gem koji će prikazivati Rcov coverage, tj. koliko je koda pokriveno testovima. + +Dodajte gemove: + + gem 'simplecov-rcov' + gem 'simplecov' + +U spec/spec_helper.rb, prije Rspec:configure bloka, treba dodati: + + require 'simplecov' + require 'simplecov-rcov' + SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter + SimpleCov.start 'rails' + +Push-ovati ovo na GitHub i sačuvati jenkins podešavanja. Sa Build now pokrećemo ***job***. Otvorimo job koji se izvršava u Build History. Pod Console Output možemo pratiti izvršavanje. + +Korisni linkovi: + + * http://gistflow.com/posts/492-jenkins-ci-setup-for-rails-application-from-scratch + * http://blogs.burnsidedigital.com/2013/01/setting-jenkins-ci-server-for-rails-project-on-a-vagrant-box/ + * http://www.webascender.com/Blog/ID/522/Setting-up-Jenkins-for-GitHub-Rails-Rspec#.U0Y-i99qNPZ + * http://artsy.github.io/blog/2012/05/27/using-jenkins-for-ruby-and-ruby-on-rails-teams/ + * http://danmcclain.net/blog/2011/11/22/using-jenkins-with-rails/ \ No newline at end of file diff --git a/logs.md b/logs.md new file mode 100644 index 0000000..43f1264 --- /dev/null +++ b/logs.md @@ -0,0 +1,149 @@ +# Upravljanje logovima + +Cilj je prepoznati sva mjesta gdje naša aplikacija kreira podatke ili gdje neki korisnik kreira podatke u aplikaciji. Zatim su nam potrebne određene komponente koje će spriječiti da ta mjesta beskonačno rastu. Tip tih komponenti, podrazumijeva se, nije isti. Dakle, potrebna nam je strategija za upravljanje izvora brzog rasta podataka. Nekada, ostaviti ih da rastu je ispravna stvar, kao na primjer, tabela zemalja svijeta. Ali, reći da ta tabela nikada neće biti prazna je, na primjer, dobar korak. +Od velikog značaja su logovi aplikacije i web servera. Ako se njima dobro ne upravlja, mogu zauzeti veliki prostor na disku. + +## Apache logovi + +Stock instalacija Apache-a koristi ***CustomLog*** direktivu da kontroliše logove HTTP zahtjeva. Lokaciju na filesystem-u gdje se logovi pojavljuju od apache2.conf možemo vidjeti sa: + + CustomLog /var/log/apache2/other_vhosts_access.log vhost_combined + +Tu se nalaze log unosi. Ali da li tu ostaju zauvijek? Zahvaljujući **log rotaciji**, ne. Log rotacija radi na sljedeći način: Cron task provjerava direktorijum konfiguracionog fajla da vidi koji fajlovi moraju da se rotiraju i onda vrši određene operacije na tim fajlovima. Na primjer, log fajl koji se zvao **secure.log** je preimenovan u **secure.log.1** a novi **secure.log** je otvoren. U sljedećoj rotaciji **secure.log.1** će biti preimenovan u **secure.log.2** i **secure.log** u **secure.log.1** a novi **secure.log** se otvara itd. + +Ovo je podrazumijevano Apache log rotacioni konfiguracioni fajl: + +**maintenance/default_apache2_logrotate.d** + + /var/log/apache2/*.log { + weekly + missingok + rotate 52 + compress + delaycompress + notifempty + create 640 root adm + sharedscripts + postrotate + if [ -f "`. /etc/apache2/envvars ; \ + echo ${APACHE_PID_FILE:-/var/run/apache2.pid}`" ]; then + /etc/init.d/apache2 reload > /dev/null + fi + endscript + } + +* Izraz za match-ovanje fajla **/var/log/apache2/*.log** govori logrotate-u koji fajlovi trebaju da se rotiraju. U ovom slučaju, svi fajlovi koji završavaju sa .log u /var/log/httpd/ direktorijumu će se rotirati. +* **weekly** direktiva govori logrotate-u da rotira ove fajlove nedjeljno. +* **rotate52** direktiva kaže da će logovi biti rotirani 52 puta prije brisanja. +* **compress** kompresuje logove pomoću ***gzip***. +* **delaycompress** odlaže kompresiju dok se fajl drugi put ne rotira. +* **notifempty** kaže da ne treba rotirati ako je log fajl prazan. +* **create 640 root adm** dodaje dozvole novokreiranim log fajlovima. +* **sharedscripts** uzrokuje da se postrotirajuća skripta pokrene samo jednom iako postoji još mnogo fajlova za rotiranje. +* **postrotate** sadrži blok shell koda koji se izvršava nakon rotiranja log fajlova. Kod uzrokuje restartovanja Apache-a. + +**reload** direktiva u **postrotate** predstavlja problem. Reload će restartovati Apache ali i Passenger (?) procese (za app u primjeru u knjizi). Ovaj problem se rješava korišćenjem Apache opcije **piped log rotation**. Apache treba da rotira glavni log bez restartovanja. Za ovo je potrebno dodati nekoliko direktiva našem Apache konfiguracionom fajlu. +Kopirajmo ***apache2.conf*** u modules/apache/files i izmijenimo CustomLog direktivu tako da je svaki log entry pisan kroz Apache **rotatelogs** i dodajemo interval u sekundama (jednom dnevno, tj. 86,400 sekundi), kada želimo da se log rotira. + + CustomLog "|/usr/sbin/rotatelogs \ + /var/log/apache2/other_vhosts_access.log 86400" vhost_combined + +U jednom Puppet file-u podesimo apache2.conf fajl i izbrišimo logrotate.d skriptu, s obrizom da pokrećemo svoju log rotaciju. + +**maintenance/modules/apache2/manifests/init.pp** + + class apache2 { + # other resources + file { + "/etc/apache2/apache2.conf": mode => "0644", + owner => "root", + group => "root", + source => "puppet:///modules/apache/apache2.conf", + notify => Service["apache2"], + require => Package["apache2"]; + "/etc/logrotate.d/apache2": + ensure => absent; + } + # other resources + } + +Pokretanjem Puppeta primijenićemo izmjene. Sada se u /var/log/apache2 nalazi novi log fajl sa timestamp-om koji je imenovan kao other_vhosts_access.log.1304985600, na primjer. + +Piped log rotation možemo koristiti i kada logovi dostignu određenu veličinu i da promijenimo format imena log fajla. Detaljnije na Apache [vebsajtu](http://httpd.apache.org/docs/2.2/programs/rotatelogs.html +). + +## Logovi Rails aplikacije + +Rails aplikacije mogu kreirati veću količinu log podataka, tako da će i ovdje biti potrebno naći način za upravljanje log fajlovima. Rails logrotate skripta ne dolazi ni sa jednim standardnim Linux paketom, tako da ćemo pokretati našu skriptu i staviti je na odgovarajuće mjesto, koristeći Puppet. + +Rotaciona skripta naše aplikacije će biti slična podrazumijevanoj skripti Apache-a, sa sljedećim razlikama: + +* Čuvaćemo samo 10 umjesto 52 posljedna log fajla. +* Dodaćemo kod za restartovanje aplikacije da kompletiramo log rotaciju. + +**maintenance/modules/apache2/templates/logrotate_rails.conf.erb** + + <%= current_path %>/log/production.log { + missingok + rotate 10 + compress + delaycompress + postrotate + touch <%= current_path %>/tmp/restart.txt + endscript + } + +U slučaju da promijenimo lokaciju aplikacije, koristićemo Puppet template u modules/massiveapp/templates/massiveapp.logrotate.conf.erb sa sljedećim sadržajem: + +**maintenance/modules/apache2/templates/logrotate_rails.conf.erb** + + <%= current_path %>/log/production.log { + missingok + rotate 10 + compress + delaycompress + postrotate + touch <%= current_path %>/tmp/restart.txt + endscript + } + +Sada dodajemo init.pp fajl modula aplikacije, koji će jednostavno postaviti rotacionu log skriptu na pravo mjesto. + +**maintenance/only_template/modules/massiveapp/manifests/init.pp** + + class massiveapp { + + $current_path = "/var/massiveapp/current" + + file { + "/etc/logrotate.d/massiveapp.conf": + owner => root, + group => root, + mode => 755, + content => template("massiveapp/massiveapp.logrotate.conf.erb") + } + } + +Najkraći ugrađeni vremenski interval koji logrotate omogućava je ***daily***, tako da se logrotate skripta automatski može pokretati jednom dnevno. Ali pomoću cron joba možemo podesiti da se skripta pokreće i češće (na primjer dva puta dnevno). + +**maintenance/modules/massiveapp/manifests/init.pp** + + class massiveapp { + $current_path = "/var/massiveapp/current" + file { + "/etc/logrotate.d/massiveapp.conf": + owner => root, + group => root, + mode => 755, + content => template("massiveapp/massiveapp.logrotate.conf.erb") + } + cron { + "massiveapp_logrotate": + command => "/usr/bin/logrotate -f /etc/logrotate.d/massiveapp.conf", + user => "vagrant", + hour => [0,12], + minute => "0" + } + } + +Sada pokretanjem Puppeta postavljamo sve na svoje mjesto, cron job-om ćemo osigurati da logovi aplikaciju imaju razumnu veličinu, a kao bonus, sada imamo framework za lako skraćivanje vremenskog intervala log rotacija ako nam je to potrebno. \ No newline at end of file diff --git a/nagios.md b/nagios.md new file mode 100644 index 0000000..b81b526 --- /dev/null +++ b/nagios.md @@ -0,0 +1,715 @@ +# Nagios + +Nagios® Core™ je Open Source aplikacija za monitoring sistema i mreže. Nadgleda hostove i servise koje odredite, obavještavajući vas kada nešto ne bude u redu ili kada nešto bude ponovo ok. Nagios Core je prvenstveno dizajniran da radi pod Linux-om, ali bi trebao da radi i pod drugim sistemima. + +Neka od mnogobrojnih svojstava Nagios Core-a su: + +* Monitoring mrežnih servisa (SMTP, POP3, HTTP, NNTP, PING...) +* Monitoring host resursa (processor load, disk usage...) +* Prost plugin dizajn koji omogućava korisnicima lak razvoj svoj check fajlova za servise +* Paralelne provjere servisa +* Mogućnost definisanja host hijerarhije pomoću 'parent' hostova, omogućavajući detekciju i praveći razliku između hostova koji ne rade i koji nisu dostupni. +* Notifikacije kada se pojave ili riješi problemi sa servisom ili hostom (putem mejla, SMS-a) +* Automatska log file rotacija +* Web interface za pregled mrežnog status, notifikacije i istorijat problema, log fajlove itd. + +Biće nam potrebna dva servera. Na jednom će se nalaziti aplikacija a na drugom, koji će nadgledati aplikaciju, Nagios. + +## Organizacija foldera +| | | +| ---------------------------| -------------------------------------------------------------------------------------------------| +|/usr/local/nagios| #osnovni direktorijum +|/usr/local/nagios/libexec| #instalirani pluginovi (iz ovog foldera se pokreću provjere servisa) +|/usr/local/nagios/bin| #sadrži osnovne fajlove za pokretanje +|/usr/local/nagios/sbin| #drugi nagios exec. fajlovi +|/usr/local/nagios/etc| #configuracioni fajlove +|/usr/local/nagios/share| #nagios web sajt fajlovi +|/usr/local/nagios/var| #logovi i drugi slični podaci + +## Osonovni nagios koncepti + +* **Host**: Server, radna stanica, mrežni uređaj... koji se nadgleda. + +**Primjer definicije hosta:** + + define host{ + host_name bogus-router + alias Bogus Router #1 + address 192.168.1.254 + parents server-backbone + check_command check-host-alive + check_interval 5 + retry_interval 1 + max_check_attempts 5 + check_period 24x7 + process_perf_data 0 + retain_nonstatus_information 0 + contact_groups router-admins + notification_interval 30 + notification_period 24x7 + notification_options d,u,r + } + +* **Host group**: grupa sličnih hostova. Npr. mogu se grupisati svi web serveri, fajl serveri itd. + +**Primjer definicije grupe hostova:** + + define hostgroup{ + hostgroup_name novell-servers + alias Novell Servers + members netware1,netware2,netware3,netware4 + } + +* **Service**: servis koji se nadgleda na hostu, kao HTTP, DNS, NFS itd. + +**Primjer definicije servisa:** + + define service{ + host_name linux-server + service_description check-disk-sda1 + check_command check-disk!/dev/sda1 + max_check_attempts 5 + check_interval 5 + retry_interval 3 + check_period 24x7 + notification_interval 30 + notification_period 24x7 + notification_options w,c,r + contact_groups linux-admins + } + +* **Servise Group**: omogućava grupisanje više servisa. Ovo je korisno za grupisanje više HTTP npr. + +**Primjer definicije grupe servisa:** + + define servicegroup{ + servicegroup_name dbservices + alias Database Services + members ms1,SQL Server,ms1,SQL Server Agent,ms1,SQL DTC + } + +* **Contact**: osoba kojoj se šalju notifikacije kada se desi ili riješi neki problem. + +**Primjer definicije kontakta:** + + define contact{ + contact_name jdoe + alias John Doe + host_notifications_enabled 1 + service_notifications_enabled 1 + service_notification_period 24x7 + host_notification_period 24x7 + service_notification_options w,u,c,r + host_notification_options d,u,r + service_notification_commands notify-by-email + host_notification_commands host-notify-by-email + email jdoe@localhost.localdomain + pager 555-5555@pagergateway.localhost.localdomain + address1 xxxxx.xyyy@icq.com + address2 555-555-5555 + can_submit_commands 1 + } + +## Instalacija + +Instalirajmo Nagios Core 4 na lokalnom računaru. + +Instalacija potrebnih paketa: + + $ sudo apt-get update + $ sudo apt-get install build-essential apache2 php5-gd wget libgd2-xpm libgd2-xpm-dev libapache2-mod-php5 sendmail daemon + + Kreirajmo sada nagios korisnika i nagcmd grupu (u koju dodajemo nagios korisnika): + + $ sudo useradd nagios + $ sudo groupadd nagcmd + $ sudo usermod -a -G nagcmd nagios + +Download Nagios Core 4.2 tar fajla: + + $ sudo wget http://prdownloads.sourceforge.net/sourceforge/nagios/nagios-4.0.2.tar.gz + $ sudo tar -xvzf nagios-4.0.2.tar.gz + +Pokrenimo sada configure sa dodatnim opcijama: + + $ cd nagios-4.0.2/ + $ sudo ./configure --with-nagios-group=nagios --with-command-group=nagcmd --with-mail=/usr/bin/sendmail + + $ sudo make all + $ sudo make install + $ sudo make install-init + $ sudo make install-config + $ sudo make install-commandmode + $ sudo make install-webconf + +Kopiramo eventhandlere i dodijelimo privilegije: + + $ sudo cp -R contrib/eventhandlers/ /usr/local/nagios/libexec/ + $ sudo chown -R nagios:nagios /usr/local/nagios/libexec/eventhandlers + +Kreirajmo Nagios korisnika za pristup internetu: + + $ sudo htpasswd -cm /usr/local/nagios/etc/htpasswd.users nagiosadmin + +Ako sa pokušamo da pokrenemo Nagios, dobićemo sljedeću grešku: + + $ sudo /etc/init.d/nagios start + /etc/init.d/nagios: 20: .: Can't open /etc/rc.d/init.d/functions + +Postoji problem sa Nagios init skriptom na Ubuntuu. Moramo izmijeniti init skriptu i promijeniti par linija. + +Kopirajte sljedeće u terminal: + + sudo sed -i "s/^\.\ \/etc\/rc.d\/init.d\/functions$/\.\ \/lib\/lsb\/init-functions/g" /etc/init.d/nagios + sudo sed -i "s/status\ /status_of_proc\ /g" /etc/init.d/nagios + sudo sed -i "s/daemon\ --user=\$user\ \$exec\ -ud\ \$config/daemon\ --user=\$user\ --\ \$exec\ -d\ \$config/g" /etc/init.d/nagios + sudo sed -i "s/\/var\/lock\/subsys\/\$prog/\/var\/lock\/\$prog/g" /etc/init.d/nagios + sudo sed -i "s/\/sbin\/service\ nagios\ configtest/\/usr\/sbin\/service\ nagios\ configtest/g" /etc/init.d/nagios + sudo sed -i "s/\"\ \=\=\ \"/\"\ \=\ \"/g" /etc/init.d/nagios + sudo sed -i "s/\#\#killproc\ \-p\ \${pidfile\}\ \-d\ 10/killproc\ \-p \${pidfile\}/g" /etc/init.d/nagios + sudo sed -i "s/runuser/su/g" /etc/init.d/nagios + sudo sed -i "s/use_precached_objects=\"false\"/&\ndaemonpid=\$(pidof daemon)/" /etc/init.d/nagios + sudo sed -i "s/killproc\ -p\ \${pidfile}\ -d\ 10\ \$exec/\/sbin\/start-stop-daemon\ --user=\$user\ \$exec\ --stop/g" /etc/init.d/nagios + sudo sed -i "s/\/sbin\/start-stop-daemon\ --user=\$user\ \$exec\ --stop/&\n\tkill -9 \$daemonpid/" /etc/init.d/nagios + +sada pokušajte ponovo da pokrenete Nagios. + +Instalirajmo sada Nagios pluginove: + + $ sudo wget http://www.nagios-plugins.org/download/nagios-plugins-1.5.tar.gz + $ sudo tar -xvzf nagios-plugins-1.5.tar.gz + $ cd nagios-plugins-1.5/ + $ sudo ./configure --with-nagios-user=nagios --with-nagios-group=nagios + $ sudo make + $ sudo make install + +Kopirajmo i linkujmo Nagios Apache konfiguraciju: + + $ sudo cp /etc/apache2/conf.d/nagios.conf /etc/apache2/sites-available/nagios + $ sudo ln -s /etc/apache2/sites-available/nagios /etc/apache2/sites-enabled/nagios + +Sada moramo promijeniti privilegije za /usr/local/nagios/var/rw/ + + $ sudo chown -R nagios:www-data /usr/local/nagios/var/rw/ + +Provjerimo sada da li ima grešaka u Nagios konfiguracijama: + + $ sudo /usr/local/nagios/bin/nagios -v /usr/local/nagios/etc/nagios.cfg + +Podesimo da se Nagios i Apache pokreću pri startovanju sistema: + + $ sudo ln -s /etc/init.d/nagios /etc/rcS.d/S98nagios + $ sudo ln -s /etc/init.d/apache2 /etc/rcS.d/S99apache2 + +Sada možemo da pokrenemo Nagios: + + $ sudo service nagios start + +kao i Apache servis: + + $ sudo service apache2 start + +Sada možemo otvoriti nagios u browseru sa **http://IPAdresa/nagios** i unesite **username** i **password** koji se kreirali. + +**NRPE** + +Nagios Remote Plug-in Executor (NRPE) je Nagios dodatak koji omogućava 'lokalne' provjere na udaljenom hostu (tj. provjere svih servisa će biti vršene na serveru gdje se nalazi Nagios). NRPE moramo instalirati na oba servera. + +Instalacija nrpe plugin-a lokalno (ili na serveru gdje se nalazi Nagios): + + $ sudo mkdir -p /usr/local/src/nrpe + $ cd /usr/local/src/nrpe + $ sudo wget http://kent.dl.sourceforge.net/project/nagios/nrpe-2.x/nrpe-2.15/nrpe-2.15.tar.gz + $ sudo tar -xf nrpe-2.15.tar.gz + $ cd nrpe-2.15 + $ sudo ./configure --with-ssl=/usr/bin/openssl--with-ssl-lib=/usr/lib/x86_64-linux-gnu + +Ako naiđete na grešku **configure: error: Cannot find ssl libraries** instalirajte sljedeće: + + $ sudo apt-get install libssl-dev + $ dpkg -L libssl-dev + $ sudo ln -s /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib/libssl.so + +Sada pokušajte configure komandu. + +Ako nije uspjelo, pokušajte sa drugim rješenjem sa [ovog linka](http://ubuntuforums.org/showthread.php?t=1975917). + +NRPE pokrećemo uz pomoć xinetd: + + $ sudo apt-get install xinetd + + $ sudo make all + + $ sudo apt-get install make (ako ne postoji make) + + $ sudo make install + $ sudo make install-daemon + $ sudo make install-daemon-config + $ sudo make install-xinetd + + $ sudo chown nagios.nagios /usr/local/nagios + $ sudo chown -R nagios.nagios /usr/local/nagios/libexec + + $ /usr/local/nagios/bin/nrpe -c /usr/local/nagios/etc/nrpe.cfg -d + + Izmijenimo /etc/xinetd.d/nrpe fajl. U only_from dodajemo IP adresu nagios servera. (samo razmak izmedju IP adresa) + +Sada možemo provjeriti da li NRPE radi na našem računaru: + + $ netstat -at | grep 5666 + $ /usr/local/nagios/libexec/check_nrpe -H 127.0.0.1 + +NRPE daemon se vezuje za port 5666. Izmijenimo iptables filter da prihvati konekciju na naš nagios server. + + $ sudo iptables -A INPUT -s 188.226.192.11 -p tcp --dport 5666 -j ACCEPT + +Instalirajmo sada NRPE na serveru. + + $ sudo useradd nagios + $ sudo groupadd nagcmd + $ sudo usermod -a -G nagcmd nagios + + $ sudo apt-get update + $ sudo apt-get install nagios-nrpe-server + $ sudo apt-get install nagios-nrpe-plugin + +Izmijenimo allowed_hosts u nrpe.cfg i dodajmo IP adresu servera i našeg računara. + +*Your IP*: http://www.whatismyip.com/ + +Restartujmo nrpe servis: + + $ sudo service nagios-nrpe-server restart + +i provjerimo da li nrpe radi sa: + + $ sudo /usr/lib/nagios/plugins/check_nrpe -H 127.0.0.1 + $ sudo /usr/lib/nagios/plugins/check_nrpe -H 107.170.116.145 + +Vratimo se na lokalna podešavanja. Otvorimo /usr/local/nagios/etc/objects. Ovjde se nalaze podešavanja za komande, hostove, kontakte i sl. Kopirajmo localhost.cfg fajl u server.cfg: + + $ sudo cp localhost.cfg server.cfg + +Izmijenimo server.cfg fajl tako da ima sljedeće: + + define host{ + use linux-server + host_name server + alias server + address 107.170.116.145 + } + + define hostgroup{ + hostgroup_name servers + alias Servers + members server + } + +i definišimo jedan servis za početak: + + define service{ + use generic-service + host_name server + service_description Current Users + check_command check_remote_users + } + +S obzirom da komanda check_remote_users ne postoji, moraćemo je sami kreirati. Izmijenimo commands.cfg fajl iz objects direktorijuma dodajući na dnu fajla: + + define command{ + command_name check_remote_users + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_users + } + +U nagios.cfg fajl uključujemo novi server: + + cfg_file=/usr/local/nagios/etc/objects/server.cfg + + +Restartujmo Nagios: + + $ sudo service nagios restart + +i otvorimo nagios u browseru: + + 127.0.0.1/nagios + +Ako dodajemo novi plugin, kopirajmo plugin-fajl u libexec folder i promijenimo dozovle: + + chown nagios:nagios plugin-name + chmod 751 plugin-name + +### Postgres + +server.cfg + + define service{ + use generic-service + host_name server + service_description Postgresql + check_command check_postgresql + } + +commands.cfg + + define command{ + command_name check_postgresql + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_pgsql + } + +On server: (in /etc/nagios/nrpe.cfg) + + command[check_pgsql]=/usr/lib/nagios/plugins/check_pgsql -H localhost -d expman_production -l expman -p secret + +### Elasticsearch cluser health + +Download: +http://exchange.nagios.org/directory/Addons/Active-Checks/Elasticsearch-cluster-status/details + +On server: + +Install elasticsearch server. +Install elasticsearch gem. + +nrpe.cfg: + + command[check_elasticsearch_cluser]=/usr/lib/nagios/plugins/check_elasticsearch_cluster.rb --ip 107.170.106.199 --port 9200 + + $ sudo service nagios-nrpe-server restart + + define service{ + use generic-service + host_name server + service_description ElasticSearch Cluster Health + check_command check_elasticsearch_cluster_health + } + + define command{ + command_name check_elasticsearch_cluster_health + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_elasticsearch_cluster + } + + $ sudo service nagios restart + +### Unicorn + +Download: + +* [link1](https://github.com/scalp42/check_unicorn/blob/master/check_unicorn) +* [link2](https://github.com/scalp42/check_unicorn/blob/master/check_unicorn_processes) + +nrpe.conf + + command[check_unicorn]=/usr/lib/nagios/plugins/check_unicorn 2 + command[check_unicorn_processes]=/usr/lib/nagios/plugins/check_unicorn_processes 2 + + define service{ + use generic-service + host_name server + service_description Unicorn + check_command check_unicorn + } + + define service{ + use generic-service + host_name server + service_description Unicorn processes + check_command check_unicorn_processes + } + + define command{ + command_name check_unicorn + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_unicorn + } + + define command{ + command_name check_unicorn_processes + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_unicorn_processes + } + +### Nginx + +Download: +https://github.com/cloved/check_nginx_status/blob/master/check_nginx_status.sh + +Enable the nginx_status page: + + $ nginx -V 2>&1 | xargs -n1 | grep status + +should return **--with-http_stub_status_module** + +Add: + + location /nginx_status { + stub_status on; + access_log off; + allow MY_IP; + deny all; + } + +in the app file in sites-enabled (nginx). + +Restart nginx and visit IP/nginx_status. + +nrpe.cfg: + + command[check_nginx_status]=sudo /usr/lib/nagios/plugins/check_nginx_status.sh IP 80 nginx_status + +change /etc/sudoers: + + %nagios ALL=(ALL) NOPASSWD:ALL + +nagios side: + + define service{ + use generic-service + host_name server + service_description Nginx status + check_command check_nginx_status + } + + define command{ + command_name check_nginx_status + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_nginx_status + } + +### Disk + +nginx.conf: + + command[check_disk]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -p / + + +### Advanced Postgres checks + +Download: +http://bucardo.org/wiki/Check_postgres + +**Connection** + + command[check_pg_connection]=/usr/lib/nagios/plugins/check_postgres.pl --action=connection --dbuser=expman --db=expman_production --dbpass=secret --host=localhost + +**Bloat** +Checks the amount of bloat in tables and indexes. (Bloat is generally the amount of dead unused space taken up in a table or index. This space is usually reclaimed by use of the VACUUM command.) + + command[check_bloat]=/usr/lib/nagios/plugins/check_postgres.pl --action=bloat --dbuser=expman --db=expman_production --dbpass=secret --host=localhost --warning='100 M' --critical='200 M' + +**Backends** + +Checks the current number of connections for one or more databases, and optionally compares it to the maximum allowed, which is determined by the Postgres configuration variable max_connections. + + command[check_backends]=/usr/lib/nagios/plugins/check_postgres.pl --action=backends --dbuser=expman --db=expman_production --dbpass=secret --host=localhost --warning='150' --critical='150' + +**database_size** +Checks the size of all databases and complains when they are too big. There is no need to run this command more than once per database cluster. + + command[check_db_size]=/usr/lib/nagios/plugins/check_postgres.pl --action=database_size --dbuser=expman --db=expman_production --dbpass=secret --host=localhost --warning='450 GB' --critical='500 GB' + +**disk_space** +Checks on the available physical disk space used by Postgres. This action requires that you have the executable "/bin/df" available to report on disk sizes, and it also needs to be run as a superuser, so it can examine the data_directory setting inside of Postgres. + +*Password for superuser postgres?* + +**query_time** +Checks the length of running queries on one or more databases. + + command[check_query_time]=/usr/lib/nagios/plugins/check_postgres.pl --action=query_time --dbuser=expman --db=expman_production --dbpass=secret --host=localhost --warning=20s --critical=40s --port=5432 + +###Default plugins + +https://nagios-plugins.org/doc/man/index.html + +**Check SSH** +Try to connect to an SSH server at specified server and port. + +**Check ping** +Use ping to check connection statistics for a remote host. + +**Check HTTP** +This plugin tests the HTTP service on the specified host. It can test normal (http) and secure (https) servers, follow redirects, search for strings and regular expressions, check connection times, and report on certificate expiration times. + +commands.cfg: + + define command{ + command_name check_ssh_nrpe + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_ssh_nrpe + } + + define command{ + command_name check_http_nrpe + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_http_nrpe + } + + define command{ + command_name check_ping_nrpe + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_ping_nrpe + } + + define command{ + command_name check_procs_nrpe + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_procs_nrpe + } + + define command{ + command_name check_load_nrpe + command_line $USER1$/check_nrpe -H $HOSTADDRESS$ -c check_load_nrpe + } + +server.cfg: + + define service{ + use generic-service + host_name server + service_description SSH + check_command check_ssh_nrpe + } + + define service{ + use generic-service + host_name server + service_description HTTP + check_command check_http_nrpe + } + + define service{ + use generic-service + host_name server + service_description PING + check_command check_ping_nrpe + } + + + define service{ + use generic-service + host_name server + service_description Current load + check_command check_load_nrpe + } + + define service{ + use generic-service + host_name server + service_description Local procs + check_command check_procs_nrpe + } + +nrpe.cfg: + + command[check_load_nrpe]=/usr/lib/nagios/plugins/check_load -w 5.0,4.0,3.0 -c 10.0,6.0,4.0 + command[check_procs_nrpe]=/usr/lib/nagios/plugins/check_procs -w 10 -c 20 --metric CPU + command[check_http_nrpe]=/usr/lib/nagios/plugins/check_http -H IP + command[check_ssh_nrpe]=/usr/lib/nagios/plugins/check_ssh IP + command[check_ping_nrpe]=/usr/lib/nagios/plugins/check_ping -H IP -w 100.0,20% -c 500.0,60% + +## Notifikacije + +Definisanje generic-contact: + +**templates.cfg** + + define contact{ + name generic-contact + service_notification_period 24x7 + host_notification_period 24x7 + service_notification_options w,u,c,r,f,s + host_notification_options d,u,r,f,s + service_notification_commands notify-service-by-email + host_notification_commands notify-host-by-email + register 0 + } + +**contacts.cfg** + + define contact{ + contact_name sgupta + use generic-contact + alias Sanjay Gupta (Developer) + email sgupta@thegeekstuff.com + pager 333-333@pager.thegeekstuff.com + } + define contact{ + contact_name jbourne + use generic-contact + alias Jason Bourne (Sysadmin) + email jbourne@thegeekstuff.com + } + + define contactgroup{ + contactgroup_name db-admins + alias Database Administrators + members jsmith, jdoe, mraj + } + + define contactgroup{ + contactgroup_name unix-admins + alias Linux System Administrator + members jbourne, dpatel, mshankar + } + +**localhost.cfg** + + define host{ + use linux-server + host_name email-server + alias Corporate Email Server + address 192.168.1.14 + contact_groups unix-admins + } + + define service{ + use generic-service + host_name prod-db + service_description CPU Load + contact_groups unix-admins + check_command check_nrpe!check_load + } + +http://linuxg.net/how-to-send-emails-via-terminal/ + + $ sudo apt-get install msmtp + $ sudo apt-get install heirloom-mailx + $ sudo apt-get install sendmail + +Kreirajte fajl ~/.msmtprc. + +host smtp.gmail.com + +port 587 + +protocol smtp + +auth on + +from Demic Redzep + +user demicredzep1990@gmail.com + +password yourpassword + +tls on + +tls_nocertcheck + + $ echo "set sendmail=/usr/bin/msmtp" > ~/.mailrc + + $ chmod 600 .msmtprc + +test: + + $ echo "Hello" | mail contact@linuxg.net + +Na osnovu ovoga, promijenite contacts.cfg. + +define command{ + command_name notify-host-by-email + command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$ + } + +# 'notify-service-by-email' command definition +define command{ + command_name notify-service-by-email + command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTALIAS$\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Service Alert: $HOSTALIAS$/$SERVICEDESC$ is $SERVICESTATE$ **" $CONTACTEMAIL$ + } + + + diff --git a/rails/controllers/routing.md b/rails/controllers/routing.md index 963aa57..b2f7f5e 100644 --- a/rails/controllers/routing.md +++ b/rails/controllers/routing.md @@ -283,8 +283,8 @@ Sljedeće rute će biti generisane: |GET|/posts/:post_id/comments(.:format)|post_comments| |POST|/posts/:post_id/comments(.:format)|post_comments| |GET|/posts/:post_id/comments/new(.:format)|new_post_comment| -|GET|/secret/comments/:id(.:format)|edit_comment| -|GET|/secret/comments/:id/edit(.:format)|comment| +|GET|/secret/comments/:id(.:format)|comment| +|GET|/secret/comments/:id/edit(.:format)|edit_comment| |PATCH/PUT|/secret/comments/:id(.:format)|comment| |DELETE|/secret/comments/:id(.:format)|comment| @@ -302,8 +302,8 @@ end |GET|/posts/:post_id/comments(.:format)|post_comments| |POST|/posts/:post_id/comments(.:format)|post_comments| |GET|/posts/:post_id/comments/new(.:format)|new_post_comment| -|GET|/comments/:id(.:format)|edit_secret_comment| -|GET|/comments/:id/edit(.:format)|secret_comment| +|GET|/comments/:id(.:format)|secret_comment| +|GET|/comments/:id/edit(.:format)|edit_secret_comment| |PATCH/PUT|/comments/:id(.:format)|secret_comment| |DELETE|/comments/:id(.:format)|secret_comment| @@ -413,14 +413,18 @@ end Prepoznaje putanje kao /comments/new/preview sa GET. + # 3 Non-Resourceful rute + Pored resource rutiranja, Rails nudi odličnu podršku za rutiranje proizvoljnih URL adresa na akcije. Ovdje se svaka ruta u aplikaciji posebno podešava. Iako se preporučuje resourceful rutiranje, postoji mnogo mjesta gdje je prikladnije koristiti obično rutiranje. + ## 3.1 Bound parametri + Kada se obična putanja podešava, predaje se niz simbola koje Rails mapira na djelova dolaznog HTTP zahtjeva. Dva od ovih simbola su posebna: :controller mapira ime kontrolera u aplikaciji i :action mapira ime akcije unutar kontrolera. Na primjer:
get ':controller(/:action(/:id))'
@@ -728,7 +732,7 @@ Takođe je moguće ograničiti štampanje spiska svih postojećih ruta na određ
$ CONTROLLER=users rake routes
-## 5.1 Testiranje ruta +## 5.2 Testiranje ruta Rails nudi tri ugrađene metode potvrde (assertions) koje olakšavaju testiranje ruta: @@ -736,14 +740,14 @@ Rails nudi tri ugrađene metode potvrde (assertions) koje olakšavaju testiranje * assert_recognizes * assert_routing -### 5.1.1 assert_generates +### 5.2.1 assert_generates assert_generates potvrđuje da određen skup opcija generiše određenu putanju i da se može koristiti sa default rutom ili nekom predefinisanom rutom:
assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' }
 assert_generates '/about', controller: 'pages', action: 'about'
-### 5.1.2 assert_recognizes +### 5.2.2 assert_recognizes assert_recognizes je suprotna assert_generates. Potvrđuje da je određena putanja prepoznata i da rutira na određeno mjesto u aplikaciji. @@ -753,11 +757,11 @@ Argument :method se može koristiti da se odredi HTTP metod:
assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', method: :post })
-### 5.1.3 assert_routing +### 5.2.3 assert_routing assert_routing provjerava rutu u oba pravca: testira da li putanja generiše opcije i da li opcije generišu putanju.
assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' })
**Izvor:** -[Rails Guides - Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html) \ No newline at end of file +[Rails Guides - Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html) diff --git a/sshd.md b/sshd.md new file mode 100644 index 0000000..3de8045 --- /dev/null +++ b/sshd.md @@ -0,0 +1,67 @@ +# Obezbjeđivanje sshd + +Novi host koji je dostupan putem Interneta može biti laka meta za hakere. Jedna od prvih stvari koje moramo uraditi, da bi se sačuvali od ***password-guessing*** ili ***dictionary*** napada, kada pokrenemo server, jeste onemogućavanja ssh password autentikacije i zahtijevanje autentikacije javnog ključa. + +Na netu postoji mnogo opisa generisanja para ključeva i uplodovanje javnog ključa. **Hint**: ***Google***. Kada javni ključ na serveru bude na mjestu, gasimo autentikaciju passworda izmjenom /etc/ssh/sshd_config, gdje mijenjamo: + + #PasswordAuthentication yes + +u + + PasswordAuthentication no + +I javljamo sshd-u da ponovo učita konfiguracioni fajlu sa: + + $ sudo /sbin/service reload sshd + +Da bismo bili sigurni, otvorićemo novi prozor u terminalu i uspostaviti novu ssh konekciju na server. Na taj način, znamo da će stvari raditi prije logouta. + +Uz pomoć Puppet manifesta konfigurišemo package-file-service vezu: + +**topics/modules/sshd/manifests/init.pp** + + class ssh { + package { + "ssh": + ensure => present, + before => File["/etc/ssh/sshd_config"] + } + file { + "/etc/ssh/sshd_config": + owner => root, + group => root, + mode => 644, + source => "puppet:///modules/ssh/sshd_config" + } + service { + "ssh": ensure => true, + enable => true, + subscribe => File["/etc/ssh/sshd_config"] + } + } + +## Rješenje #2 + +[Izvor](http://serverfault.com/questions/312500/how-do-i-configure-sshd-on-debian-to-use-public-key-authentication) + + Izmijenimo /etc/ssh/sshd_config fajl da budeo kao: + + # Both of these are probably already there, but commented + PubkeyAuthentication yes + # The next line makes sure that sshd will look in + # $HOME/.ssh/authorized_keys for public keys + AuthorizedKeysFile %h/.ssh/authorized_keys + # Again, this rule is already there, but usually defaults to 'yes' + PasswordAuthentication no + +Kreiranje para ključeva: [How to setup ssh keys](https://www.digitalocean.com/community/articles/how-to-set-up-ssh-keys--2) + +Nakon toga, **restart ssh** sa: + + /etc/init.d/sshd restart + +Prije svega navedenog morao je biti kreiran .ssh direktorijum sa odgovarajućom dozvolom. (chmod 0700 za ~/.ssh a i chmod 0600 za authorized_keys) + +Onemogućavanje SSH password autentikacije za određene korisnike ili Linux grupe: + +[Xmodulo članak](http://xmodulo.com/2013/03/how-to-force-ssh-login-via-public-key-authentication.html) \ No newline at end of file diff --git a/youtube.md b/youtube.md new file mode 100644 index 0000000..c20465b --- /dev/null +++ b/youtube.md @@ -0,0 +1,374 @@ +# YouTube on Rails + +YouTube je treći najposjećeniji sajt na svijetu. U ovom tutorijalu, naučićemo kako da napravimo aplikaciju koja omogućava korisnicima dodavanje i gledanje video klipova sa YouTube-a. Informacije o video klipovima će biti dostupne preko YouTube API-ja. Video klipovi će biti prikazani preko standardnom YouTube playera uz pomoć YouTube IFrame API-ja. + +* Izvorni kod na GitHub-u: [SitePoint](https://github.com/bodrovis/SitePoint-YtVideos) +* Demo verzija na Heroku-u: [SitePoint Heroku App](http://sitepoint-yt-videos.herokuapp.com/) + +Za početak, kreirajmno novu Rails aplikaciju: + + $ rails new yt_videos -T + +-T će kreirati aplikaciju bez default test fajlova. + +U Gemfile-u dodajemo bootstrap i youtube_it gem koji radi sa YouTube API. + + gem 'bootstrap-sass', '~> 3.1.1' + gem 'youtube_it', '~> 2.4.0' + + $ bundle install + +Dodajemo: + + @import "bootstrap"; + +u custom.css.scss fajl u stylesheet folderu. + +**Video** model treba da sadrži sljedeće atribute: + +* id – integer, primarni ključ, indeksiran, unique +* link – string +* uid – string. Dodaćemo i ovdje index i unique. +* title – string. Naslov YouTube videa sa youtube sajta. +* author – string. Ime autora videa sa youtube sajta. +* duration – string. Dužina trajanja videa u formatu: “00:00:00″ +* likes – integer. Broj lajkova. +* dislikes – integer. Broj dislajkova. + +Kreirajmo model: + + $ rails g model Video link:string title:string author:string duration:string likes:integer dislikes:integer + $ rails g migration add_uid_to_videos uid:string:uniq:index + +routes.rb: + + resources :videos, only: [:index, :new, :create] + root to: 'videos#index' + +application.html.erb: + +
+ <% flash.each do |key, value| %> +
+ + <%= value %> +
+ <% end %> +
+ +index.html.erb + +
+
+

Share your videos with the world!

+

Click the button below to share your video from YouTube.

+

+ <%= link_to 'Add video now!', new_video_path, class: 'btn btn-primary btn-lg' %> +

+
+
+ +videos_controller.rb + + class VideosController < ApplicationController + + def new + @video = Video.new + end + + def create + @video = Video.create(video_params) + if @video.save + flash[:success] = 'Video added!' + redirect_to root_url + else + render 'new' + end + end + end + + private + + def video_params + params.require(:video).permit(:uid, :link, :title, :author, :duration, :likes, :dislikes) + end + +new.html.erb + +
+

New video

+ + <%= form_for @video do |f| %> + <%= render 'shared/errors', object: @video %> + +
+ <%= f.label :link %> + <%= f.text_field :link, class: 'form-control', required: true %> + A link to the video on YouTube. +
+ + <%= f.submit class: 'btn btn-default' %> + <% end %> +
+ + +shared/_errors.html.erb + + <% if object.errors.any? %> +
+
+

The following errors were found while submitting the form:

+
+ +
+ +
+
+ <% end %> + +Da dohvatimo informacije iz YouTube API-ja, biće nam potreban jedinstveni id videa. Taj id se već nalazi u URL-u videa. Ali URL može imati više formata. Ovo su validni YT URL formati: + +http://www.youtube.com/watch?v=0zM3nApSvMg&feature=feedrecgrecindex +http://www.youtube.com/user/IngridMichaelsonVEVO#p/a/u/1/QdK8U-VIH_o +http://www.youtube.com/v/0zM3nApSvMg?fs=1&hl=en_US&rel=0 +http://www.youtube.com/watch?v=0zM3nApSvMg#t=0m10s +http://www.youtube.com/embed/0zM3nApSvMg?rel=0 +http://www.youtube.com/watch?v=0zM3nApSvMg +http://youtu.be/0zM3nApSvMg + +Da dohvatimo ovaj ID, koristićemo sljedeći regularni izraz: + +video.rb + + YT_LINK_FORMAT = /\A.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/i + + validates :link, presence: true, format: YT_LINK_FORMAT + +taj ID treba da dohvatimo prije dodavanje videa: + +video.rb + + before_create -> do + uid = link.match(YT_LINK_FORMAT) + self.uid = uid[2] if uid && uid[2] + + if self.uid.to_s.length != 11 + self.errors.add(:link, 'is invalid.') + false + elsif Video.where(uid: self.uid).any? + self.errors.add(:link, 'is not unique.') + false + else + get_additional_info + end + end + +Sada se moraju dohvatiti informacije o video klipu: + +video.rb + +private + + def get_additional_info + begin + client = YouTubeIt::OAuth2Client.new(dev_key: 'Your_YT_developer_key') + video = client.video_by(uid) + self.title = video.title + self.duration = parse_duration(video.duration) + self.author = video.author.name + self.likes = video.rating.likes + self.dislikes = video.rating.dislikes + rescue + self.title = '' ; self.duration = '00:00:00' ; self.author = '' ; self.likes = 0 ; self.dislikes = 0 + end + end + + def parse_duration(d) + hr = (d / 3600).floor + min = ((d - (hr * 3600)) / 60).floor + sec = (d - (hr * 3600) - (min * 60)).floor + + hr = '0' + hr.to_s if hr.to_i < 10 + min = '0' + min.to_s if min.to_i < 10 + sec = '0' + sec.to_s if sec.to_i < 10 + + hr.to_s + ':' + min.to_s + ':' + sec.to_s + end + +Ovdje se uključuje youtube_it. Prvo se kreira client promjenljiva za upite. Ovdje će nam biti potreban YouTube developer ključ za upite sa javnim YouTube podacima. Da dobijete svoj developer ključ, registrujte novu aplikaciju na https://code.google.com/apis/console. Otvorite podešavanja i otvorite ''APIs and auth'' pa zatim Credentials. Kreirajte novi javni ključ za browser aplikaciju koji će biti vaš developer ključ. +Nakon inicijalizacije klijenta, videu se pristupa sa video_by gdje će biti dostupne sve informacije. Trajanje video klipa će biti predstavljeno u sekundama tako da će nam biti potrebna parse_duration metoda zbog formatiranja. + +Do sada, aplikacija omogućava dodavanje novih video klipova, dohvata informacije o tom klipu a dodali smo i neke validacije. + +## Prikaz + +U videos_controller.rb dodajemo: + + def index + @videos = Video.order('created_at DESC') + end + +I u view-u: + + <% if @videos.any? %> +
+

Latest videos

+ +
+ + <% @videos.in_groups_of(3) do |group| %> +
+ <% group.each do |video| %> + <% if video %> +
+
+ <%= image_tag "https://img.youtube.com/vi/#{video.uid}/mqdefault.jpg", alt: video.title, + class: 'yt_preview img-rounded', :"data-uid" => video.uid %> + +
+
<%= video.title %>
+

Author: <%= video.author %>

+

Duration: <%= video.duration %>

+

+ <%= video.likes %> + <%= video.dislikes %> +

+
+
+
+ <% end %> + <% end %> +
+ <% end %> +
+ <% end %> + +**#player-wrapper** je prazan blok gdje će se nalaziti YT player. in_groups_of govori koliko će se video klipova nalaziti u jednom redu. Preview slika može imati tri formata: + +* https://img.youtube.com/vi/mqdefault.jpg – 320×180 slika bez crnih traka ispod i iznad slike. +* https://img.youtube.com/vi/hqdefault.jpg – 480×360 slika sa crnim trakama ispod i iznad slike. +* https://img.youtube.com/vi/<1,2,3>.jpg – 120×90 slika sa više scena iz klipa sa crnim trakama ispod i iznad slika. + +Da prikažemo klipove na sajtu, koristime Youtube IFrame API. + +U application.html.erb dodajemo: + + + + + + +U javascripts/yt_player.coffee dodajemo: + + jQuery -> + $('.yt_preview').click -> makeVideoPlayer $(this).data('uid') + + # Initially the player is not loaded + window.ytPlayerLoaded = false + + _run = -> + # Runs as soon as Google API is loaded + $('.yt_preview').first().click() + return + + $(window).bindWithDelay('resize', -> + player = $('#ytPlayer') + player.height(player.width() / 1.777777777) if player.size() > 0 + return + , 500) + + makeVideoPlayer = (video) -> + if !window.ytPlayerLoaded + player_wrapper = $('#player-wrapper') + player_wrapper.append('

Loading player...

') + + window.ytplayer = new YT.Player('ytPlayer', { + width: '100%' + height: player_wrapper.width() / 1.777777777 + videoId: video + playerVars: { + wmode: 'opaque' + autoplay: 0 + modestbranding: 1 + } + events: { + 'onReady': -> window.ytPlayerLoaded = true + 'onError': (errorCode) -> alert("We are sorry, but the following error occured: " + errorCode) + } + }) + else + window.ytplayer.loadVideoById(video) + window.ytplayer.pauseVideo() + return + + google.setOnLoadCallback _run + + return + + +1.7(7) je 16:9 rezolucija. + +Koristi se bindWithDelay funkcija koju moramo definisati: + +javascript/jquery.bind_with_delay.js + + /* + bindWithDelay jQuery plugin + Author: Brian Grinstead + MIT license: http://www.opensource.org/licenses/mit-license.php + + http://github.com/bgrins/bindWithDelay + http://briangrinstead.com/files/bindWithDelay + + Usage: + See http://api.jquery.com/bind/ + .bindWithDelay( eventType, [ eventData ], handler(eventObject), timeout, throttle ) + + Examples: + $("#foo").bindWithDelay("click", function(e) { }, 100); + $(window).bindWithDelay("resize", { optional: "eventData" }, callback, 1000); + $(window).bindWithDelay("resize", callback, 1000, true); + */ + + (function($) { + + $.fn.bindWithDelay = function( type, data, fn, timeout, throttle ) { + + if ( $.isFunction( data ) ) { + throttle = timeout; + timeout = fn; + fn = data; + data = undefined; + } + + // Allow delayed function to be removed with fn in unbind function + fn.guid = fn.guid || ($.guid && $.guid++); + + // Bind each separately so that each element has its own delay + return this.each(function() { + + var wait = null; + + function cb() { + var e = $.extend(true, { }, arguments[0]); + var ctx = this; + var throttler = function() { + wait = null; + fn.apply(ctx, [e]); + }; + + if (!throttle) { clearTimeout(wait); wait = null; } + if (!wait) { wait = setTimeout(throttler, timeout); } + } + + cb.guid = fn.guid; + + $(this).bind(type, data, cb); + }); + }; + + })(jQuery); \ No newline at end of file