Wednesday, May 27, 2015

Veeam Backup и PowerShell. Бэкапы по расписанию.

Чудный продукт Veeam Backup and Replication, начиная с версии 8 (Patch 2) открыл небольшую лазейку для совершения бэкапов по расписанию, точнее, VeeamZIP.
Точнее - в бесплатной версии заработал cmdlet  Start-VBRZip.
Итак, чем это грозит и как это можно использовать. А можно просто напилить скрипт, который будет делать полные бэкапы виртуальных машин. Ну и запихнуть его в планировщик.

Естественно, надо установить Veeam PowerShellSDK. Ну и в самом Veeam настроить все сервера.

Собственно, сам скрипт:
 param (
    [string]$csvFile = 'C:\ToBackup.txt', 
    [int]$storeDays = 7
    )

$doVMBackup = {
    param ($VMHost, $VMName, $VMStorePath, $VMLimitStore)
    try {
        Add-PSSnapin VeeamPSSnapin
        $viServerObject = Get-VBRServer -Name $VMHost
        $viObject = Find-VBRViEntity -Server $viServerObject -Name $VMName
        Start-VBRZip -Folder $VMStorePath -Entity $viObject
        if ($VMLimitStore -ne -1) {

            $limit = (Get-Date).AddDays(-$VMLimitStore)
            # Delete files older than the $limit.
            Get-ChildItem -Path $VMStorePath -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | Remove-Item -Force

            # Delete any empty directories left behind after deleting the old files.
            Get-ChildItem -Path $VMStorePath -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }) -eq $null } | Remove-Item -Force -Recurse
        }
    }
    catch {
        write-host "An error occured...."
    }
}

$VMTotal = Import-Csv $csvFile
foreach ($VM in $VMTotal) {
    Start-Job -ScriptBlock $doVMBackup -ArgumentList $VM.Host,$VM.Name,$VM.Path,$storeDays



Скрипт, как параметр принимает 2 параметра - CSV файл c машинами, которые надо бэкапить и срок хранения бэкапов. Естественно, этот механизм можно переделать под себя.
Благодаря использованию Job задачи идут параллельно :)

Формат CSV файла:
Host, Name, Path
"10.10.10.10","MyVM","C:\BKPTest"
"<Host name in Veeam>","<VM name>","<Path to Backup>"

Спасибо ресурсам
http://memos.nomblot.org/veeam
http://go.veeam.com/learn-powershell-basics-free-tutorial-course/
Хабру и StackOverflow :)

Wednesday, May 6, 2015

Сервис определения страны по IP

Итак, возникла необходимость вычислять, так сказать по IP. А точнее - определять страну для автоматической подстановки телефонного кода. Как оказалось, за эту услугу хотят брать денюжки, например тут. Но хочется то халявного. Ну и по общим правилам опенсорса, если что-то надо - напиши сам. Тем более, база, пусть и не такая точная есть у того же MaxMind
Есть также и такая штука, но я что-то не очень уверен в ее скорости. Но можно попробовать.
В любом случае, родился такой сервис.
Абсолютно бесплатно, поддерживает https.
Используется связка Nginx + uwsgi + flask + postgresql. Почему так? Потому что хотелось попробовать, что есть фласк и постгре. Написано крайне коряво, потому что только учусь. Сейчас получается каждый запрос - это отдельное подключение к базе. Т.к. это все-таки сервер есть мнение переписать это под вариант одно подключение - много запросов и подключение держать постоянно. Но как это сделать я пока не знаю, посмотрим на нагрузку.
Ну и по традиции - код. Да, я знаю, он ужасен, в связи с чем попрошу в комментах наставить на путь истинный.

--
upd:

  • Постарался сделать так, чтобы соединение к базе было одно, а не дергать каждый раз.
  • Добавил метод автоопределения места запроса https://geoip.webcall.today/getcountry?ip=auto



from flask import Flask,request,jsonify
import socket
import psycopg2

def ip_address_is_valid(address):
    try: socket.inet_aton(address)
    except socket.error: return False
    else: return address.count('.') == 3

application = Flask(__name__)
con  = psycopg2.connect(database='geoip', user='geoip', host='localhost', password = 'geoipuser')

@application.route('/')
def hello_world():
    return "<b>Geoserver welcomes you. Usage = https://geoip.webcall.today/getcountry?ip=X.X.X.X</b><br> This product includes GeoLite2 data created by MaxMind, available from<br> <a href=\"http://www.maxmind.com\">http://www.maxmind.com</a>."

@application.route('/getcountry')
def getcountry():
    global con
    ip = request.args.get('ip', '')
    if ip == 'auto':
        ip = '%s' % request.remote_addr
    if ip_address_is_valid(ip):
        try:
            cur = con.cursor()
            cur.execute("SELECT code,location,fullname FROM countrydata WHERE geodata = (SELECT geodata FROM ipdata WHERE subnet >>= '%s'::inet LIMIT 1) LIMIT 1" % ip)
            result = cur.fetchone()
            if result:
                result = jsonify(code = result[0],
                                   location = result[1],
                                   name = result[2]), 200
            else:
                result = jsonify(code = 'none',
                                 location = 'None',
                                 name = 'None'), 404
        except psycopg2.DatabaseError, e:
            if con:
                con.rollback()
            con  = psycopg2.connect(database='geoip', user='geoip', host='localhost', password = 'geoipuser')
            result = jsonify(code = 'Error %s' % e,
                            location = 'None',
                            name = 'None'), 500
        finally:
            pass
    else:
        result = jsonify(code = 'none',
                         location = 'None',
                         name = 'None'), 404
    return result

if __name__ == '__main__':
    application.run(host='127.0.0.1')


Ну и ip_address_is_valid можно переписать с использованием регулярок. Что-то типа

--import socket
++import re

def ip_address_is_valid(address):
    regex_ip = "^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$"
    if re.match(regex_ip, address):
        return True
    return False