#!/usr/bin/env python2.7
########################################################################################
# Jadm license information
#
'''
Copyright (c) <2015>, <Nikolay Georgiev Dachev> <nikolay@dachev.info>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
########################################################################################
# Python modules
# datetime - used for current date/time on event
# tabulate - used to print useful infomration in table format
# os, sys, subprocess - used for comunication with FreeBSD
# netifaces - used for check FreeBSD bridge interfaces
# ipaddress - used to check if jail ip is from gateway network
# cmd - used for jadm command shell
# paramiko - used for jail migration via ssh
# socket - jadm client/server
# urllib - download freebsd bin install files
# hashlib library (md5 file check)

from datetime import datetime
from tabulate import tabulate
from ipaddress import *
import sys
import os
import logging
import subprocess
import netifaces
import cmd
import copy
# network server
import hashlib
import string
import random
import threading
import paramiko
import socket
import select
import urllib
import argparse

########################################################################################
# Main config vars:
########################################################################################

# jadm version
jadm_version = "ver. 2.1.3"

# jail.conf location by default /etc/jail.conf
jailconf = '/etc/jail.conf'

# jadm log file
logfile = '/var/log/jadm.log'

# jadm don't show verbose output by default when start or stop jail
verbose = 0

# auto confirm when use cli
autoc = 'n/a'

# settings start index in jail.conf
_Lsettings = '#@#localsettings#@#'

# jadm server
LHOST = '0.0.0.0'
LPORT = 4555

# network config

# vnet
vnet = [
'### network settings ###',
'vnet;',
'vnet.interface = "epair${jid}b";',
'exec.start = "ifconfig lo0 127.0.0.1/8";',
'exec.prestart = "ifconfig epair${jid} create";',
'exec.prestart += "ifconfig $bridge addm epair${jid}a up";',
'exec.prestart += "ifconfig epair${jid}a up";',
'exec.start += "ifconfig epair${jid}b $jip";',
'exec.start += "route add default $jgw";',
'exec.start += "/bin/sh /etc/rc";',
'exec.poststop = "ifconfig $bridge deletem epair${jid}a";',
'exec.poststop += "ifconfig epair${jid}a destroy";',
'exec.stop = "/bin/sh /etc/rc.shutdown";',
'exec.clean;',
'persist;'
]

dhcp = [
'### network settings ###',
'vnet;',
'vnet.interface = "epair${jid}b";',
'exec.start = "ifconfig lo0 127.0.0.1/8";',
'exec.prestart = "ifconfig epair${jid} create";',
'exec.prestart += "ifconfig $bridge addm epair${jid}a up";',
'exec.prestart += "ifconfig epair${jid}a up";',
'exec.start += "dhclient epair${jid}b";',
'exec.start += "/bin/sh /etc/rc";',
'exec.poststop = "ifconfig $bridge deletem epair${jid}a";',
'exec.poststop += "ifconfig epair${jid}a destroy";',
'exec.stop = "/bin/sh /etc/rc.shutdown";',
'exec.clean;',
'persist;'
]

# no vnet
net = [
'### network settings ###',
'ip4.addr = "$jip";',
'exec.start = "/bin/sh /etc/rc";',
'exec.stop = "/bin/sh /etc/rc.shutdown";',
'persist;',
]

# nfs fstab settings
remnfs = 'nfs rw,hard,intr,bg,rsize=8192,wsize=8192,tcp 0 0'

########################################################################################
# Main Functions
########################################################################################

def jadm_postinstall(new_jail_path):
   """
########################################################################################
# customize jails after installation finish
# do it after create_jail(install_path) finish
# you can customize it if you know how!
#
   """
# simple echo to new jail /etc/ config
   os.system("echo '# Added by JADM' >> %s" % ospath(new_jail_path+"/etc/rc.conf"))
   os.system("echo 'sendmail_enable=\"NONE\"' >> %s" % ospath(new_jail_path+"/etc/rc.conf"))
   os.system("echo 'firewall_enable=\"YES\"' >> %s" % ospath(new_jail_path+"/etc/rc.conf"))
   os.system("echo 'firewall_script=\"/etc/rc.firewall\"' >> %s" % ospath(new_jail_path+"/etc/rc.conf"))
   os.system("echo 'firewall_type=\"OPEN\"' >> %s" % ospath(new_jail_path+"/etc/rc.conf"))
   os.system("touch %s" % ospath(new_jail_path+"/etc/resolv.conf"))
   os.system("echo 'nameserver 8.8.8.8' >> %s" % ospath(new_jail_path+"/etc/resolv.conf"))
   log(0, "postinstall was completed!")

def ospath(path):
    """
########################################################################################
# try to fix OS path var
    """
    fxpath = os.path.normpath(path)
    return fxpath

def load_jail_config(conf_f):
   """
########################################################################################
# load_jail_config - load default jail conf file and return a list with jail dictionary .
#  {'name':'jail name', 'host.hostname':'jail hostname', 'jid':'jail id', '$jip':'jail ip address ',
#  '$jgw':'jail gateway','path':'jail home dir', 'vnet':'is this vnet jail', 'zfs':'is thi zfs jail', ... }
#
   """

# check if jail.conf exist
   if os.path.isfile(conf_f) != True:
      log(2, "'%s' not exist!" % conf_f)
      log(0, "'%s' was created!" % conf_f)
      os.system("echo '# This file is managed by JADM %s' > %s" % (jadm_version, conf_f))
      os.system("echo '#' >> %s" % (conf_f))
      #sys.exit(2)
# open jail.conf
   jc = open(conf_f, 'r')

# take all elements from jail.conf, remove "junk" data and applay to mas[]
# only data which will be used is taken, all valuse are in order
   mas = []
   not_sorted_mas = []
   for i in jc:
      i = i.strip('\n')
      i = i.strip('\t')
# create raw list from file ony \n is removed
      not_sorted_mas.append(i)
      i = i.strip(';').replace('\"', '').replace(' ', '')
      i = i.split('=')
      mas.append(i)
   jc.close()

# find all jails names
   jsname = []
# enumerate in order to avoid wrong index for similiar items like {
# take only elements which are before { symbol
   for i, x in enumerate(mas):
      if "{" in x:
        jsname.append([mas[i - 1]])

# check active/dying jails
   caj = subprocess.check_output("jls -h jid", shell=True).strip('jid')
   caj = caj.split('\n')
   caj = cleanlist(caj)
# check for 'dying' jails
   caj_dying = subprocess.check_output("jls -d -h jid", shell=True).strip('jid')
   caj_dying = caj_dying.split('\n')
   caj_dying = cleanlist(caj_dying)

# find all jail.conf global settings (ignore # comments)
# search jail.conf from 0 line to first jail{} line
   global_settings = []
   if jsname != []:
      gs_only = not_sorted_mas[0:mas.index(jsname[0][0])]
      for i in gs_only:
         if '#' in i:
            pass
         else:
# global seting line number
            x = not_sorted_mas.index(i)
# glabal setting table format
            i = i.strip(';').replace(' ', '')
            if i:
               global_settings.append([x, i])

# Jails Configuration in dict format.
   jails = []

   for i in jsname:
      jail_dict = {'name':i[0][0], 'host.hostname':None, 'jid':'', '$jip':'',
                   '$jgw':'','path':'', 'zfs':0}

      i = mas.index(i[0])
      jail_dict['jbegin'] = i

# Cehck jail index's (end and local_settigs)
      x = 0
      jail_local_settings_index = 0
      for z in mas[jail_dict['jbegin']:]:
         x = x + 1
# Find jail local settingss
         if z[0] == _Lsettings:
            jail_local_settings_index = jail_dict['jbegin'] + x
# Find if this is linked jail
         if '#<' in z[0]:
             jail_dict['LINKED'] = z[0].strip('#<')
# Find jail end index
         if "}" in z:
            x -= 1
            jail_dict['jend'] = jail_dict['jbegin'] + x
            break

# add local settings index on firs place in list (us original jc[] )
      jlsettings_list = []
      i = jail_local_settings_index
      if i != 0:
         for x in not_sorted_mas[i : jail_dict['jend']]:
            i += 1
            if '#' in x:
               pass
            else:
# i is line number in jail.conf
               x = x.strip(';')#.replace(' ', '')
               if x:
                  jlsettings_list.append([i, x])

      jail_dict['local_settings'] = jlsettings_list

# All main jail settings
      for x in mas[jail_dict['jbegin']:jail_dict['jend']]:
         if x[0] in jail_dict.keys():
            jail_dict[x[0]] = x[1]

# is this a vnet jail ?
         if x[0] == 'vnet':
            jail_dict['vnet'] = 1

# do we have a bridge interface?
         if x[0] == '$bridge':
            jail_dict['$bridge'] = x[1]

# is this zfs jail ?
         if x[0] == 'path':
# if is SKEL jail will contain /mounts we don't need this
             jail_dict['path'] = ospath(jail_dict['path'].replace('/mnt', ''))
             zfs_check = zfs(jail_dict['path'])
             jail_dict['zfs'] = zfs_check[2]

# is this SKELETON MODEL jail
         if x[0] == 'mount.fstab':
            if '/rw/etc/fstab' in x[1]:
               jail_dict['SKEL'] = 1

# check if jail is active/inactive or dying
      if jail_dict['jid'] in caj:
         act = 1
      else:
         act = 0
         if jail_dict['jid'] in caj_dying:
            act = 'dying'
      jail_dict['active'] = act

# try to find jail path if we use global path with $name (jail name)
      if jail_dict['path'] == '':
         jail_dict['path'] = find_global_path(global_settings, jail_dict['name'])

# check if this is zfs
      zfs_check = zfs(jail_dict['path'])
      jail_dict['zfs'] = zfs_check[2]

# check for inside jail hostname config
      if jail_dict['host.hostname'] == None or jail_dict['host.hostname'] == "":
         if jail_dict['active'] == 1:
          try:
             inside_hostname = subprocess.check_output(['jexec', jail_dict['jid'], 'hostname'])
             jail_dict['host.hostname'] = inside_hostname
          except:
             pass
# end add this jail and go with the next one
      jails.append(jail_dict)

# return a list with all jails from jail.conf and list with all global settings
   return (jails, global_settings)

def find_global_path(gs, jname):
   """
########################################################################################
# try to find jail path from global path='' setting
#
   """
   for x in gs:
      if 'path' in x[1]:
         gpath = x[1]
         for i in ('$name', '${name}'):
            if i in gpath:
               gpath = gpath.strip('path').strip('=').strip('"').strip("'").replace(i, jname)
               return ospath(gpath)

def zfs(zvalue = None):
   """
########################################################################################
# check if zvalue is in OS zfs list (mountpoint or zfs name)
# return (zfs names, zfs mountpoints,
#         zfs ( 0 if not exist) or [NAME,USED,AVAIL,REFER,MOUNTPOINT,QUOTA, SNAP_NUMBER])
#
   """

# check for zfs.ko
   try:
      subprocess.check_output("kldstat -m zfs", shell=True, stderr=subprocess.STDOUT)
   except subprocess.CalledProcessError as e:
      return (None, None, 0)

# check curent zfs pool
   zfs_name = subprocess.check_output("zfs list -H -o name", shell=True)
   zfs_name = zfs_name.split('\n')

   zfs_mountp = subprocess.check_output("zfs list -H -o mountpoint", shell=True)
   zfs_mountp = zfs_mountp.split('\n')

# default is False
   iszfs = 0
# cehck if iszfs exist
   if zvalue in zfs_name:
      return (zfs_name, zfs_mountp, 1)
# check if zvalue is in zfs(path or mountpoint)
   if zvalue in zfs_mountp:
# get zfs - NAME USED  AVAIL  REFER  MOUNTPOINT
      iszfs = subprocess.check_output("zfs list -H %s" % zvalue, shell=True)
      iszfs = iszfs.strip('\n').split('\t')
# get zfs quota
      iszfs_quota = subprocess.check_output("zfs get -H quota %s" % iszfs[0], shell=True)
      iszfs_quota = iszfs_quota.strip('\n').split('\t')
      iszfs.append(iszfs_quota[2])
# get numbet of snapshots
      zfs_snap = 0
      zfs_snap_search = iszfs[0]+'@'
      cmd = "zfs list -t snapshot |grep %s" %  (zfs_snap_search)
      try:
         for x in subprocess.check_output(cmd, shell=True).split('\n'):
            zfs_snap = zfs_snap + 1
         zfs_snap = zfs_snap - 1
      except:
         zfs_snap = 0
      iszfs.append(zfs_snap)

   return (zfs_name, zfs_mountp, iszfs)

def jail_table(mcmd):
   """
########################################################################################
# show all jails settings in tbale format
# show individual jail settings in table format
#
   """
   jails  = load_jail_config(jailconf)[0]

   jails_configs = []
   if len(mcmd) > 1:
       if mcmd[1] in ('ip', 'gw'):
           mcmd[1] = '$j' + mcmd[1]
       if mcmd[1] == 'hostname':
           mcmd[1] = 'host.hostname'

   if len(mcmd) == 3:
       for i in jails:
# list olny jails with quota / usage >= from user search
          if i['zfs'] != 0:
              if mcmd[1] == 'quota':
                  if mcmd[2][-1] not in ('K', 'M', 'G', 'T', 'P', 'E'):
                     return False
                  jquota = float(i['zfs'][5][:-1])
                  jquota = bytesto(jquota, i['zfs'][5][-1])
                  search_quota = float(mcmd[2][:-1])
                  search_quota = bytesto(search_quota, mcmd[2][-1])
                  if int(jquota) >= int(search_quota):
                    jails_configs.append(i)

              elif mcmd[1] == 'used':
                  if mcmd[2][-1] not in ('K', 'M', 'G', 'T', 'P', 'E'):
                     return False
                  jused = float(i['zfs'][1][:-1])
                  jused = bytesto(jused, i['zfs'][1][-1])
                  search_used = float(mcmd[2][:-1])
                  search_used = bytesto(search_used, mcmd[2][-1])
                  if int(jused) >= int(search_used):
                     jails_configs.append(i)
# find all other jail settings
          if mcmd[1] not in i.keys():
              pass
          elif mcmd[2] in i[mcmd[1]]:
              jails_configs.append(i)

          jails = jails_configs

   if len(mcmd) == 2:
       for i in jails:
           if mcmd[1] == 'active':
               if i['active'] == 1:
                  jails_configs.append(i)
           elif mcmd[1] == 'inactive':
               if i['active'] == 0:
                  jails_configs.append(i)
           elif mcmd[1] == 'dying':
               if i['active'] == 'dying':
                  jails_configs.append(i)
           elif mcmd[1] == i['name']:
               jails_configs.append(i)
           elif mcmd[1] == i['jid']:
               jails_configs.append(i)
           elif mcmd[1] == 'zfs':
               if i['zfs'] != 0:
                  jails_configs.append(i)
           elif mcmd[1] == 'nozfs':
               if i['zfs'] == 0:
                  jails_configs.append(i)
           elif mcmd[1] == 'skel':
               if 'SKEL' in i.keys():
                  jails_configs.append(i)
           elif mcmd[1] == 'noskel':
               if 'SKEL' not in i.keys():
                  jails_configs.append(i)
           elif mcmd[1] == 'nolinkedskel':
               if 'SKEL' not in i.keys():
                  if 'LINKED' not in i.keys():
                     jails_configs.append(i)
           elif mcmd[1] == 'linked':
               if 'LINKED' in i.keys():
                  jails_configs.append(i)
           elif mcmd[1] == 'nolinked':
               if 'LINKED' not in i.keys():
                  jails_configs.append(i)
           elif mcmd[1] == 'vnet':
               if 'vnet' in i.keys():
                  jails_configs.append(i)
           elif mcmd[1] == 'novnet':
               if 'vnet' not in i.keys():
                  jails_configs.append(i)
           elif mcmd[1] == 'empty':
               if '' in i.values():
                  jails_configs.append(i)
           jails = jails_configs

   if len(jails) == 0:
       return False

# create a jails table list
   jlist = []
   for i in jails:
       jail_temp_list = []
       jail_temp_list.append('')
       if os.path.exists("%s/remote.info.jadm" % i['path']):
           jail_temp_list[0] =  jail_temp_list[0] + 'R'
       if 'SKEL' in i.keys():
           jail_temp_list[0] =  jail_temp_list[0] + 'S'
       if 'vnet' in i.keys():
           jail_temp_list[0] =  jail_temp_list[0] + 'V'
       if 'LINKED' in i.keys():
           jail_temp_list[0] =  jail_temp_list[0] + 'L%s' % i['LINKED']
       jail_temp_list.append(i['active'])
       jail_temp_list.append(i['jid'])
       jail_temp_list.append(i['name'])
       jail_temp_list.append(i['host.hostname'])
       if i['$jip'] == 'dhcp':
           if i['active'] == 1:
                out = subprocess.check_output(["jexec",  i['name'], "ifconfig", "epair%sb" % i['jid']])
                for ip in out.split(" "):
                    if ch_ipv4(ip) == True and ip[-3:] != '255':
                        dhip = ip
                jail_temp_list.append('dhcp (%s)' % dhip)
           else:
                jail_temp_list.append('dhcp')
       else:
           jail_temp_list.append(i['$jip'])
       jail_temp_list.append(i['$jgw'])
       jail_temp_list.append(i['path'])
       if i['zfs'] != 0:
          jail_temp_list[0] =  jail_temp_list[0] + 'Z'
          jail_temp_list.append('%s/%s' % (i['zfs'][1], i['zfs'][2]))
          jail_temp_list.append(i['zfs'][-2])
          jail_temp_list.append(i['zfs'][-1])
       else:
          jail_temp_list.append('n/a')
          jail_temp_list.append('n/a')
          jail_temp_list.append('n/a')
       if i['active'] == 'dying':
          jail_temp_list[0] = jail_temp_list[0] + '_DYING'

       if mcmd[0] == 'jls':
           jail_temp_list = jail_temp_list[:6]
       jlist.append(jail_temp_list)

# table menu
   lmen = ["FLAGS", "ACTIVE", "JID", "NAME", "HOSTNAME", "IP ADDRESS", "GATEWAY", "PATH", "USED/AVAIL SPACE", "ZQUOTA",  "ZSNAP"]
# check if we use short list - jls
   if mcmd[0] == 'jls':
       lmen = ["FLAGS", "ACTIVE", "JID", "NAME", "HOSTNAME", "IP ADDRESS"]

# sorte list by x[2] - jid
   jlist = sorted(jlist, key=lambda x: x[2])
# print table
   print tabulate(jlist, lmen)
   return jlist

def bytesto(bytes, to, bsize=1024):
   """
########################################################################################
# convert bytes to megabytes, etc.
# sample code:
# print('mb= ' + str(bytesto(314575262000000, 'm')))
#
# sample output:
# mb= 300002347.946
# from: https://gist.github.com/shawnbutts/3906915
   """

   a = {'K' : 1, 'M': 2, 'G' : 3, 'T' : 4, 'P' : 5, 'E' : 6 }
   r = float(bytes)
   for i in range(a[to]):
      r = r * bsize

   return(r)

def cleanlist(list):
   """
########################################################################################
# remove all empty '' elements from list
#
   """
   list = [x for x in list if x]
   return list

def random_mac():
    """
########################################################################################
# generate and return random ether interface mac address 02:ff:c0
#
    """
    macaddr = [ 0x02, 0xff, 0xc0,
        random.randint(0x00, 0x7f),
        random.randint(0x00, 0xff),
        random.randint(0x00, 0xff) ]
    return ':'.join(map(lambda x: "%02x" % x, macaddr))

def ch_ipv4(sp):
    """
########################################################################################
# This function check if ip address fromat sp var is correct
# numbers of octet should be 4, each octet separated by '.' should be less than 256
#
    """
    octet = sp.split('.')
    if len(octet) != 4:
       return False
    try:
       return all(0<=int(p)<256 for p in octet)
    except ValueError:
       return False

def gateways(gwtable = None):
   """
########################################################################################
# find and display in table format existing bridge interfacess in system
#
   """

   bridges = []
   bridge_dict = {'name':'', 'addr':''}
# find all bridegX interfaces
   for i in netifaces.interfaces():
      if 'bridge' in i:
# fill all lists for bridge
         try:
            bridge_dict['addr'] = netifaces.ifaddresses(i)[netifaces.AF_INET]
            for x in bridge_dict['addr']:
               bridges.append({'name':i, 'addr':x})
         except KeyError:
            bridges.append({'name':i, 'addr':'0.0.0.0'})

# append os system default gateway
   system = subprocess.check_output('netstat -nr', shell=True)
   system = system.split('\n')
   for i in system:
      i = i.split(' ')
      if 'default' in i[0]:
         bridges.append({'name':'system', 'addr':{'addr':i[12].replace(' ', ''), 'broadcast':'', 'netmask':'' }})

# make final list TO BE FIXED
   gwlist = []
   gwname = 'n/a'
   for i in  bridges:
      if i['name'] == gwname:
          i['name'] = ' '
      if i['addr'] == '0.0.0.0':
         gwlist.append([i['name'], 'none', 'none', 'none'])
      else:
         gwlist.append([i['name'], i['addr']['addr'], i['addr']['netmask'], i['addr']['broadcast']])

# show gateway table
   if gwtable != None:
      lmen = ['INTERFACE', 'ADDRESS', 'NETWORKMASK', 'BROADCAST']
      print tabulate(gwlist, lmen)      

# return bridge interface name, ip addresses and network mask
   return (bridges)

def bridge_network_check(ip, bridge_ip, bridge_netmask):
   """
########################################################################################
# check if ipaddress is in selected bridge network
#
   """
# convert vars to unicode
   ip = unicode(ip)
   bridge_ip = unicode(bridge_ip)
   bridge_netmask = unicode(bridge_netmask)
# by default ip is not in bridge network
   brctl = 0

# bridge insterface ip network
   brdige_network = IPv4Interface('%s/%s' % (bridge_ip, bridge_netmask)).network

# check if ip is from bridge network and return bridge control var (brctl) = true
   if IPv4Address(ip) in list(IPv4Network(brdige_network)):
      brctl = 1

# return brctl and bridge ip network
   return [brctl, brdige_network]

def globals(action = None, setting = None):
   """
########################################################################################
#  manipulate jail global options list, remove or add new option
#
   """
   jail_config = load_jail_config(jailconf)
   jails, gsettings = jail_config[0], jail_config[1]
# create globals list  
   num = 0
   gs = []
   for i in gsettings:
      gs.append([num, i[1]])
      num += 1

   lmen = ["GLOBALS SETTINGS"]
# show globals
   if action == None:
      print tabulate(gs, lmen)

# add new global setting
   elif action == 'add':
       if "path" in setting:
           print setting.split('/')
           if '$name' != setting.split('/')[-1]:
               log(2, "no correct global path!")
               log(0, "example: 'path=/jails_path/$name'")
               return False
       if setting != None:
          if gsettings == []:
              gs_position = 0
          else:
              gs_position = gsettings[-1][0]
          update_jail_conf(['insert_line', [gs_position, '%s;' % setting]])
          log(0, "'%s' global setting was added!" % setting, 1)

# remove existing global setting
   elif action == 'remove':
      try:
         rmset = gsettings.pop(int(setting))
         update_jail_conf(['rm_line', [rmset[0]]])
         log(1, "'%s' glabal setting was removed!" % rmset[1])
      except:
         print tabulate(gs, lmen)
         log(2, "'%s' is not a global setting number!" % setting, 1)
         return False
   else:
      log(2, "Please enter 'help globals' for valid commands!")

def locals(j = None, action = None, setting = None):
   """
########################################################################################
#  manipulate jail locals options list, remove or add new option
#
   """
   jails = load_jail_config(jailconf)[0]

# find jail by id or name
   jail = find_jail(str(j))
   if jail == False:
      return False

# create jail locals list
   num = 0
   ls = []
   for i in jail['local_settings']:
      ls.append([num, i[1]])
      num += 1

   lmen = ["'%s'" % jail['name'], "LOCALS SETTINGS"]

# only return local settings in list
   if action == 'noop':
      return ls

# list jail local settings
   if action == None:
      if ls == []:
        log(0, "'%s' jail doesn't have locals settings!" % (jail['name']))
      else:
         print tabulate(ls, lmen)

# add new jail local setting
   elif action == 'add':
       if setting != None:
          if jail['local_settings'] == []:
              ls_position = jail['jend']
          else:
              ls_position = jail['local_settings'][-1][0]
          update_jail_conf(['insert_line', [ls_position, '%s;' % setting]])
          log(0, "'%s' local setting was added to '%s' jail!" % (setting, jail['name']), 1)
# update local jail /etc/jail.conf
# ljail = jail['name']
          update_local_jail_conf(jail['name'])
# remove existing jail local setting
   elif action == 'remove':
      try:
         rmset = jail['local_settings'].pop(int(setting))
         update_jail_conf(['rm_line', [rmset[0] - 1]])
         log(1, "'%s' local setting was removed from '%s' jail!" % (rmset[1], jail['name']), 1)
# update local jail /etc/jail.conf
# ljail = jail['name']
         update_local_jail_conf(jail['name'])
      except:
         print tabulate(ls, lmen)
         log(2, "'%s' is not a '%s' jail local setting number!" % (setting, jail['name']))
         return False
   else:
      log(2, "Please enter 'help locals' for valid commands!")


def update_jail_conf(update):
   """
########################################################################################
# update jail.conf
#
   """
   jails = load_jail_config(jailconf)[0]
   dt = str(datetime.now())
   mset = "BASE"

# make a backup
   subprocess.call(['cp', '-f', jailconf, '/tmp/jail.conf.jadm_bck'])

# open jail.conf
   jc = open(jailconf, 'r')
   jcl = []
   for i in jc:
      i = i.strip('\n')
      jcl.append(i)
   jc.close()

   if update[0] == 'insert_line':
      jcl.insert(update[1][0], update[1][1])

   if update[0] == 'rm_line':
      del jcl[update[1][0]]

   if update[0] == 'rm_jail':
      jail = update[1]
      del jcl[jail['jbegin']:jail['jend'] + 1]

# create jail in jails.conf format and update it!
   if update[0] == 'new_jail':
      jail = update[1][0]
      ljail = jail['name']
      jail_conf = [' ', jail['name'],
                   '{',
                   '# %s ' % dt,
                   'jid = %s;' % jail['jid'],
                   '$jip = "%s";' % jail['$jip'],
                   '$jgw = "%s";' % jail['$jgw'],
                   'host.hostname = "%s";' % jail['host.hostname']]
# if is linked jail
      if jail['install'][0] == 'linked':
         jail_conf.insert(3, "#< %s" % jail['install'][2])
         mset = 'LINKED'
# if we add remote jail
      if jail['install'][0] == 'nfs':
         jail['install'][0] = 'skel'
         mset = "REMOTE"
# jail path add /mnt is this is skel jail
      if jail['install'][0] == 'skel':
         jail_conf.extend(['path = "%s/mnt";' % jail['path']])
      else:
         jail_conf.extend(['path = "%s";' % jail['path']])
# jail networking
      if jail['vnet'] == 1:
         jail_conf.extend(['$bridge = "%s";' % jail['$bridge']])
         if jail['$jip'] == 'dhcp':
             jail_conf.extend(dhcp)
         else:
             jail_conf.extend(vnet)
      else:
         jail_conf.extend(net)
# if this is skel jail
      if jail['install'][0] == 'skel':
         jail_conf.extend(['### %s mount settings ###' % mset, 'mount.fstab = %s/rw/etc/fstab;' % jail['path'], 'mount.devfs;'])
# if is linked jail
      if jail['install'][0] == 'linked':
         jail_conf.extend(['### %s mount settings ###' % mset, 'mount.fstab = %s/etc/fstab;' % jail['path'], 'mount.devfs;'])
# update jlc[]
      if update[2] == 'create':
# if this is remote jail
         if mset == "REMOTE":
            jail_conf.extend(['#@# local settings #@#'])
            for i in jail['local_settings']:
               if i != None:
                  jail_conf.append('%s;' % i[1])
            jail_conf.append('}')

         elif jail['vnet'] == 1:
             jail_conf.extend(['#@# local settings #@#', 'exec.prestart += "ifconfig epair${jid}b ether %s";' % random_mac(), '}', ' '])
         else:
             jail_conf.extend(['#@# local settings #@#', '}', ' '])
         jcl.extend(jail_conf)

      if update[2] == 'modify':
         oldjail = update[1][1]

         jail_conf.extend(['#@# local settings #@#'])
         for i in oldjail['local_settings']:
            if i != None:
               jail_conf.append('%s;' % i[1])
         jail_conf.append('}')

         del jcl[oldjail['jbegin']:oldjail['jend'] + 1]
         lnum = oldjail['jbegin']
         for i in jail_conf:
            jcl.insert(lnum, i)
            lnum += 1
# print info
      log(0, "jail.conf was updated for '%s'" % jail['name'])

# close jail.conf
   jcl = cleanlist(jcl)
   jcl = [x for x in jcl if x not in [' ', '', '    ']]
   jc = open(jailconf, 'w')
   for i in jcl:
       jc.write("%s\n" % i)
       if i == '}':
          jc.write("\n")
   jc.close()

# update local jail /etc/jail.conf
   ljail = jail['name']
   if update[0] == 'new_jail':
      update_local_jail_conf(ljail)

def update_local_jail_conf(jail):
   """
########################################################################################
#  keep local /jail_paht/etc/jail.conf up to date
#
   """
   jails = load_jail_config(jailconf)[0]
   for i in jails:
       if i['name'] == jail:
           jail = i

   dt = str(datetime.now())
   jconf = '%s/etc/jail.conf' % jail['path']
   if 'SKEL' in jail.keys():
       jconf = '%s/rw/etc/jail.conf' % jail['path']

   jail_conf = [' ', jail['name'],
                   '{',
                   '# %s ' % dt,
                   'jid = %s;' % jail['jid'],
                   '$jip = "%s";' % jail['$jip'],
                   '$jgw = "%s";' % jail['$jgw'],
                   'host.hostname = "%s";' % jail['host.hostname']]
# if is linked jail
   if 'LINKED' in jail.keys():
      jail_conf.insert(3, "#< %s" % jail['LINKED'])
      mset = 'LINKED'
# jail path add /mnt is this is skel jail
   if 'SKEL' in jail.keys():
         jail_conf.extend(['path = "%s/mnt";' % jail['path']])
   else:
         jail_conf.extend(['path = "%s";' % jail['path']])
# jail networking
   if 'vnet' in jail.keys():
         jail_conf.extend(['$bridge = "%s";' % jail['$bridge']])
         if jail['$jip'] == 'dhcp':
             jail_conf.extend(dhcp)
         else:
             jail_conf.extend(vnet)
# if this is skel jail
   if 'SKEL' in jail.keys():
         jail_conf.extend(['### BASE mount settings ###', 'mount.fstab = %s/rw/etc/fstab;' % jail['path'], 'mount.devfs;'])
# if is linked jail
   if 'LINKED' in jail.keys():
      jail_conf.extend(['### %s mount settings ###' % mset, 'mount.fstab = %s/etc/fstab;' % jail['path'], 'mount.devfs;'])
# add local settings
   jail_conf.extend(['#@# local settings #@#'])
   for i in jail['local_settings']:
       if i != None:
          jail_conf.append('%s;' % i[1])
# end of jconf
   jail_conf.append('}')

   jc = open(jconf, 'w')
   for i in jail_conf:
       jc.write("%s\n" % i)
   jc.close()

   log(0, "'%s' was updated!" % jconf)

def log(ltype, msg, logit = None):
   """
########################################################################################
# print msg and log it if is needed
# log([0 - INFO, 1 = WARRNING and 2 - ERROR], 'log message', 'eny value if you want
#      to log in jailog file'
#
   """

   logtype = ['INFO', 'WARNING', 'ERROR']
   print "	%s: %s" % (logtype[ltype], msg)

   if logit != None:
      logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.DEBUG, filename=logfile)
      if ltype == 0:
         logging.info(msg)
      if ltype == 1:
         logging.warning(msg)
      if ltype == 2:
         logging.error(msg)

def jail_input(modify = None):
   """
########################################################################################
# create new jail or modify existing jail (settings input)
#
   """
   load_jails = load_jail_config(jailconf)
   jails, gsettings = load_jails[0], load_jails[1]
   new_jail = {}

   if modify == None:
      prompt = ':> '
      acText = 'Create New'
      input_keys = ['name', 'id', 'hostname','vnet', 'ip', 'zfs', 'path', 'install src']
      settings_menu = ['Jail', 'Setting']
      new_jail = {'name':'', 'host.hostname':'', 'jid':'', '$jip':'',
               '$jgw':'system','path':'', 'zfs':['none', 'none', 'none'], 'vnet':0, 'gwnetmask':'', 'linked':0}
      modjail = {'name':'', 'host.hostname':'', 'jid':'', '$jip':'',
               '$jgw':'','path':'', 'zfs':['', '', ''] }
   else:
      modjail = check_jail_Setting('modify', modify)
      if modjail == False:
         return False
# fix if is not vnet
      if 'vnet' not in modjail.keys():
         modjail['vnet'] = ''
# fix if is not 'zfs'
      if modjail['zfs'] == 0:
         modjail['zfs'] = ['none', 'none', 'none']
#add empty 'install'
      modjail['install'] =  ['', '']
      acText = "Modify '%s'" % modjail['name']
      input_keys = ['name', 'id', 'hostname','vnet', 'ip', 'zfs', 'path']
      settings_menu = ['Jail', 'Setting', 'Old Setting']
# copy jail settings when we modify them
      new_jail = copy.deepcopy(modjail)

   print "----------------------------------------------------"
   print "                %s Jail " % acText
   print "----------------------------------------------------"

   for ikey in input_keys:
       while True:

# choose install source - only for new jail
           if ikey == 'install src':
             print ""
             print tabulate([['template', 'existing jail for template' ], ['bsd',  'bdsinstaller'],  ['src', 'bsd pre compiled sources /usr/src/' ],
                            ["skel", "jail skeleton model"], ["linked", "unionfs linked jail"]], ['Install Source',  'Description'])

           if modify != None:
              input_keys_correction = ['name', 'jid', 'host.hostname','vnet', '$jip', 'zfs', 'path']
              jindex = input_keys_correction[input_keys.index(ikey)]
# display current jail setting
              cr_prompt = modjail[jindex]

              if ikey == 'vnet':
                 if modjail['vnet'] == 1:
                     yn_prompt = 'is vnet'
                 else:
                     yn_prompt = 'is not vnet'
                 cr_prompt = yn_prompt

              prompt = "(%s) :> " % cr_prompt
              if ikey in ['vnet']:
                 ekey = raw_input("is %s jail (y/n) %s" % (ikey, prompt))
                 if 'y' not in ekey:
                     break
              else:
                 if ikey == 'zfs':
                    if modjail['zfs'][0] != 'none':
                       ekey = 'y'
                    else:
                       ekey = 'n/a'
                 else:
                    ekey = raw_input('enter jail %s %s' % (ikey, prompt))
# if new settings == old settings
              if ekey in ['', ' ', '   ']:
                 if ikey == 'vnet':
                     if modjail['vnet'] == 1:
                        ekey = 'y'
                 else:
                    new_jail[jindex] == ekey
                    break
           else:
# main prompt start here
              if ikey == 'vnet' or ikey == 'zfs':
                 ekey = raw_input("is %s jail (y/n) %s" % (ikey, prompt))
                 if 'y' not in ekey:
                     break
              elif ikey == 'path':
                 ekey = raw_input('enter jail root %s %s' % (ikey, prompt))
              else:
                 ekey = raw_input('enter jail %s %s' % (ikey, prompt))
# exit
           if '!' in ekey:
              log(0, 'user exit - !')
              return False

# enter jail name
           if ikey == 'name':
              if check_jail_Setting(ikey, ekey) == False:
                 continue
              else:
                 new_jail['name'] = ekey
                 break
# enter jail id
           if ikey == 'id':
              if check_jail_Setting(ikey, ekey) == False:
                 continue
              else:
                 new_jail['jid'] = str(ekey)
                 break
# enter jail hostname
           if ikey == 'hostname':
              if check_jail_Setting(ikey, ekey) == False:
                 pass
              else:
                 new_jail['host.hostname'] = ekey
                 break
# enter jail vnet gateway or be system
           if ikey == 'vnet':
              if 'y' == ekey:
                 while True:
                    print ''
                    gws = gateways('table')
# if we modify exsiting jail
                    if modify != None:
                        egw = raw_input("enter jail gateway (%s) :> " % modjail['$jgw'])
                        if egw in ['', ' ', '   ']:
                            egw = modjail['$jgw']
                    else:
                       egw = raw_input("enter jail gateway %s" % prompt)
# exit
                    if '!' in egw:
                       log(0, 'user exit - !')
                       return False
# check if entered gw exist
                    find_gw = check_jail_Setting(ikey, [egw, gws])
                    if find_gw == False:
                       continue
                    else:
                       new_jail['$jgw'] = find_gw[0]
                       new_jail['$bridge'] = find_gw[1]
                       new_jail['gwnetmask']= find_gw[2]
                       new_jail['vnet'] = 1
                       break
                 break

# enter ip address already 
           if ikey == 'ip':
              if 'dhcp' in ekey:
                 if new_jail['vnet'] != 1:
                     log(1, "'dhcp' can be used only with 'vnet' jail!")
                     continue
                 new_jail['$jip'] = 'dhcp'
                 break
              ip = check_jail_Setting(ikey, [ekey, new_jail, modjail])
              if ip == False or ip == "":
                 continue
              else:
                 if ip == modjail['$jip'][:-3]:
                    new_jail['$jip'] = '%s%s' % (ip, modjail['$jip'][-3:])
                 else:
                    new_jail['$jip'] = ip
                 break
# create zfs if is needed
           if ikey == 'zfs':
              if 'y' in ekey:
                 while True:
# if we modify exsiting jail
                    if modify != None:
                        ezfs = raw_input("enter zfs root (%s) :> " % modjail['zfs'][0])
                        if ezfs in ['', ' ', '   ']:
                           new_jail['zfs'][0] = modjail['zfs'][0]
                           break
                    else:
                        ezfs = raw_input("enter zfs root %s" % prompt)
                        if ezfs in ['', ' ', '   ']:
                            continue
# exit
                    if '!' in ezfs:
                       log(0, 'user exit - !')
                       return False
# try to use global setting path as zfs:
                    if ezfs == '':
                       ezfs = find_global_path(gsettings, new_jail['name'])
                       log(0, "global path found - '%s' will be used!" % ezfs)

                    ezfs_check = check_jail_Setting(ikey, [ezfs, new_jail['name']])
                    if ezfs_check == False:
                       continue
                    else:
                       new_jail['zfs'][0] = '%s/%s' % (ezfs, new_jail['name'])
                       break
# use zfs quota -----------
                 while True:
# if we modify exsiting jail
                          if modify != None:
                             zquota = raw_input("enter zfs quota (%s) :> " % modjail['zfs'][-2])
                             if zquota in ['', ' ', '   ']:
                                zquota = modjail['zfs'][-2]
                          else:
                             zquota = raw_input("enter zfs quota %s" % prompt)
# exit
                          if '!' in zquota:
                             log(0, 'user exit - !')
                             return False
                          if  zquota in ['\n', '', ' ', '	']:
                             new_jail['zfs'][-2] = 'none'
                             break
                          if zquota == 'none':
                              break
                          if  check_jail_Setting('zquota', zquota) == False:
                             continue
                          else:
                             new_jail['zfs'][-2] = zquota
                             break
# use zfs quota -----------
                 break
              break
# enter jail path
           if ikey == 'path':
# try to use global setting path as zfs:
              if ekey == '':
                 ekey = find_global_path(gsettings, new_jail['name'])
                 log(0, "global path found - '%s' will be used!" % ekey)

              elif ekey == '':
                 log(2, " not valid path!")
                 continue
              else:
# jail path = /root path/jail name
                 ekey = '%s/%s' % (ekey, new_jail['name'])

              if check_jail_Setting(ikey, ekey) == False:
                 continue
              else:
                 if '/' != ekey[0]:
                     log(2, "'%s' is not valid path!" % ekey)
                     continue
                 new_jail['path'] = ospath(ekey)
                 if new_jail['zfs'][0] != 'none':
                    new_jail['zfs'][2] = ekey
                 break

# choose install source - only for new jail
           if ikey == 'install src':
             if check_jail_Setting(ikey, ekey) == False:
                 continue
             else:
                 new_jail['install'] = [ekey, ' ']
                 break

# print existing jails table if we will use template for install
   if new_jail['install'][0] == 'template' or new_jail['install'][0] == 'linked':
      while True:
         print ""
         if new_jail['install'][0] == 'linked':
            jail_table(['jls', 'nolinkedskel'])
         else:
            jail_table(['jls'])
         ekey = raw_input('enter %s name/jid %s' % (new_jail['install'][0], prompt))
# exit
         if '!' in ekey:
            log(0, 'user exit - !')
            return False
         template =  check_jail_Setting('template', ekey)
         if template == False:
            continue
         else:
            new_jail['install'][1] = template
            if new_jail['install'][0] == 'linked':
               new_jail['linked'] == [ekey]
            break

# print settings before confirm
   while True:
      print " "
      men_jail_settings = [['Name', new_jail['name'], modjail['name']],
                           ['JID', new_jail['jid'], modjail['jid']],
                           ['Hostname', new_jail['host.hostname'], modjail['host.hostname']],
                           ['IP', new_jail['$jip'], modjail['$jip']],
                           ['Gateway', new_jail['$jgw'], modjail['$jgw']],
                           ['Path', new_jail['path'], modjail['path']]]
      if modify == None:
          men_jail_settings.append(['Install src:', '%s %s' % (new_jail['install'][0], new_jail['install'][1])])
      if new_jail['zfs'][0] != 'none':
          men_jail_settings.append(['Zfs', new_jail['zfs'][0], modjail['zfs'][0]])
          men_jail_settings.append(['Zfs Quota', new_jail['zfs'][-2], modjail['zfs'][-2]])
      print tabulate(men_jail_settings, settings_menu)
      print " "
      yn = raw_input("confirm (y):>")
      if 'y' not in yn:
         log(0, 'user exit - !')
         return False
      else:
# check if we have changes:
         modjail_diff = 0
         for i in modjail.keys():
            if new_jail[i] != modjail[i]:
               modjail_diff = 1
         if modjail_diff == 0:
            log(0, "no changes in '%s' settings" % modjail['name'])
            return False

# proceede new_jail
         if modify == None:
            create_jail(new_jail)
         else:
            new_jail['modify'] = modjail['name']
            modify_jail(new_jail)
# exit
         return False

def check_jail_Setting(setting, val):
   """
########################################################################################
# check if we can use 'val' for jail 'settings'
#
   """

   load_jails = load_jail_config(jailconf)
   jails, gsettings = load_jails[0], load_jails[1]

# check if modify jail/jid exist
   if setting == 'modify':
      for j in jails:
         if j['name'] == val:
            return j
         if j['jid'] == val:
            return j
      if val == 'BASE':
         return False
      log(2, "'%s' is a not existing jail!" % val)
      return False

# check if we can use name = val
   if setting == 'name':
      exist = []
      for j in jails:
         exist.append (j['name'])
      if val in exist:
         log(2, "Jail with %s '%s' already exist!" % (setting, val))
         return False
      if val == 'BASE':
         log(2, "'BASE' cannot be used for jail name!")
         return False
      if val in [' ', '', ' ']:
         log(2, "jail name cannot be empty!")
         return False

# check if we can use id = val
   if setting == 'id':
      try:
         int(val)
         if val == '0':
            log(2, "'%s' is not a valid jail id number!" % (val))
            return False
      except:
         log(2, "'%s' is not a valid jail id number!" % (val))
         return False
      exist = []
      for j in jails:
         exist.append (j['jid'])
         if val in exist:
            log(2, "Jail with %s '%s' already exist!" % (setting, val))
            return False

# cehck if we can use hostname = val
   if setting == 'hostname':
       exist = []
       for j in jails:
          exist.append (j['host.hostname'])
       if val in ['', ' ', '    ']:
           log(1, "Jail with empty hostname!")
       if val in exist:
          log(1, "Jail with %s '%s' already exist!" % (setting, val))

# cehck vnet = val and return gateway
   if setting == 'vnet':
       if ch_ipv4(val[0]) == False:
          log(2, "'%s' is not a valid gateway ip address!" % (val[0]))
          return False
       for j in val[1]:
          gaddr = j['addr']
          if val[0] == gaddr['addr']:
              return (gaddr['addr'], j['name'], gaddr['netmask'])
       log(2, "'%s' is not a valid gateway ip address!" % (val[0]))
       return False

# check if this ip is available
# val[0] - ip, vla[1] new_jail{}
   if setting == 'ip':
#  create ip list
      jip = val[0].split(',')
      mjip = val[1]['$jip'].split(',')

      for ip in jip:
         ip = ip.replace(' ', '')
# check if mod ip is the same
         if ip in mjip:
            continue

         if ch_ipv4(ip) == False:
            log(2, "'%s' is not a valid ip address!" % (ip))
            return False
# check if we use this ip address in other jail
         for j in jails:
            exjip = j['$jip'].split(',')
            for i in exjip:
               if ip == i:
                  log(2, "'%s' ip address is used by '%s' jail!" % (ip, j['name']))
                  return False
# check is we use vnet
         if val[1]['$jgw'] != 'system':
            br_network = bridge_network_check(ip, val[1]['$jgw'], val[1]['gwnetmask'])
            if br_network[0] == 0:
               log(2, "'%s' is not a part from %s/%s network!" % (ip, val[1]['$jgw'], val[1][ 'gwnetmask']))
               return False
            if ip == val[1]['$jgw']:
               log(2, "'%s' ip address is same as selected gateway!" % ip)
               return False
            mask = str(br_network[1]).split('/')
            return_ip = "%s/%s" % (ip, mask[1])
      return val[0]

# check jail zfs root -----------------
   if setting == 'zfs':
# get/create zfs
      if zfs('%s/%s' % (val[0], val[1]))[2] == 1:
          log(2, "'%s/%s' already exist and cannot be used!" % (val[0], val[1]), 1)
          return False
      if zfs(val[0])[2] != 1:
         log(2, "'%s' zfs root not exist!" % val[0])
         if autoc != 'y':
            zfs_y = raw_input('create it now (y)?> ')
         else:
             zfs_y = 'y'
         if 'y' in zfs_y:
            try:
               subprocess.check_call('zfs create %s' % val[0], shell=True)
               subprocess.check_call('zfs set mountpoint=none  %s' % val[0], shell=True)
               log(0, "jail zfs root '%s' was created!" % val[0], 1)
               return True
            except subprocess.CalledProcessError:
               log(2, "'%s' cannot be created!" % val[0], 1)
               return False
         else:
            return False
      else:
         return True

# check zfs quota
   if setting == 'zquota':
      if val[-1] in ('K', 'M', 'G', 'T', 'P', 'E') or val == 'none':
         try:
            int(zquota[:-1])
            return True
         except:
            pass
      else:
         log(2, "'%s' is not correct zfs quota format!  xxx('K', 'M', 'G', 'T', 'P', 'E') " % val)
         return False

# check if we can use path = val
   if setting == 'path':
       if zfs(val)[2] == 1:
         log(2, "'%s' already exist zfs path!" % val)
         return False

       if os.path.isdir(val) == True:
         log(2, "'%s' already exist!" % val)
         return False

# check install source
   if setting == 'install src':
       install_src = ['bsd', 'template', 'skel', 'src', 'linked']
       if val not in install_src:
          log(2, "enter only  %s for install source!" % install_src)
          return False
       if val == 'template' or val == 'linked':
          if jails == False:
              log(2, "Jail list is empty!")
              return False
       if val == 'skel':
          if jails != False:
             for j in jails:
                 if 'BASE' == j['name']:
                    return True
          log(2, "'BASE' jail not exist!")
          print "   Please create it first before start to use 'skeleton' model!"
          return False
       if val == 'src':
          log(1, "'src' will use pre build freebsd sources from /usr/src")
          print "   please use 'create srcbuild' before continue if 'make buildworld'"
          print "   is not done on this system or do this manually"
          print ""

# check if selected template exist
   if setting == 'template':
      for j in jails:
         if val in j.values():
             if j['active'] == 0:
                return j['name']
             else:
                log(2, "'%s' is running and cannot be used as template" % j['name'])
                return False
      log(2, "'%s' is not existing jail!" % val)
      return False

def create_jail(newjail):
   """
########################################################################################
#  create new jail
#
   """
   jails = load_jail_config(jailconf)[0]
   print
# 1. create jail home folder
   subprocess.call(['mkdir', '-p', '%s' % newjail['path']])
   log(0 ,"jail home folder '%s' was created!" % newjail['path'])

# 2. if we use zfs
   if newjail['zfs'][0] != 'none':
      subprocess.call(['zfs', 'create', '%s' % newjail['zfs'][0]])
      subprocess.call(['zfs', 'set', 'mountpoint=%s' % newjail['path'], '%s' % newjail['zfs'][0]])
      subprocess.call(['zfs', 'set', 'jailed=on', '%s' % newjail['zfs'][0]])
      subprocess.call(['zfs', 'jail', newjail['jid'], '%s' % newjail['zfs'][0]])
      log(0 ,"jail zfs '%s' was created!" % newjail['zfs'][0])

      if newjail['zfs'][1] != 'none':
          subprocess.call(['zfs', 'set', 'quota=%s' % newjail['zfs'][1], '%s' % newjail['zfs'][0]])
          log(0 ,"jail zfs quota '%s' was set for '%s'" % (newjail['zfs'][1], newjail['zfs'][0]))

# 3. select install method
   if newjail['install'][0] == 'bsd':
      hwplatform = subprocess.check_output(['uname', '-m']).strip('\n')
      release =  subprocess.check_output(['uname', '-r'])[:4].strip('\n')
      os.environ["BSDINSTALL_DISTSITE"] = "http://ftp.freebsd.org/pub/FreeBSD/releases/%s/%s-RELEASE" % (hwplatform, release)
      subprocess.call(['bsdinstall', 'jail', '%s' % newjail['path']])

   if newjail['install'][0] == 'src':
      os.chdir("/usr/src/")
      subprocess.call(['make', 'installworld', 'DESTDIR=%s' % newjail['path']])
      subprocess.call(['make', 'distribution', 'DESTDIR=%s' % newjail['path']])

   if newjail['install'][0] == 'skel':
# find BASE jail
      for i in jails:
         if i['name'] == 'BASE':
            base = i
            break
      if base == None:
          log(2, "'BASE' jail not exist!")
          return False
# create skel jail
      subprocess.call(['mkdir', '-p', '%s/rw' % newjail['path']])
      subprocess.call(['mkdir', '-p', '%s/mnt' % newjail['path']])
      subprocess.call(['cp', '-R', '%s/SROOT/' % base['path'], '%s/rw' % newjail['path']])

# create sekl jail fstab file
      base_jail_fstab(base['path'], newjail['path'])

   if newjail['install'][0] == 'linked':
       link = check_jail_Setting('modify', newjail['install'][1])
       if 'SKEL' in link.keys():
           log(2, "link: '%s' is a skeleton model jail! " % newjail['install'][1])
           log(2, "skeleton jail cannot be used for linked jail!")
           return False
       newjail['install'].append(link['jid'])
       linked_jail_fstab(link['path'], newjail['path'])
       log(0, "jail was installed from link:' %s' " % newjail['install'][1])

   if newjail['install'][0] == 'template':
# 1 find template
       template = check_jail_Setting('modify', newjail['install'][1])
# 2. template copy data
       subprocess.call(['cp', '-R', '%s/' % template['path'], '%s/' % newjail['path']])
# 3. check if our template is skeleton jail
       if 'SKEL' in template.keys():
         log(0, "template: '%s' is a skeleton model jail! " % newjail['install'][1])
         for i in jails:
            if i['name'] == 'BASE':
               base = i
               break
         if base == None:
            log(2, "'BASE' jail not exist!")
            return False
# create sekl jail fstab file
         base_jail_fstab(base['path'], newjail['path'])
         newjail['install'][0] = 'skel'
       log(0, "jail was installed from template:' %s' " % newjail['install'][1])
   else:
       print " "
       log(0, "jail was installed from '%s'" % newjail['install'][0])

# 4. do post install
   if newjail['install'][0] != 'template':
      if newjail['install'][0] == 'skel':
          jadm_postinstall('%s/rw' % newjail['path'])
      else:
          jadm_postinstall(newjail['path'])

# 5. update jail.conf
   update_jail_conf(['new_jail', [newjail], 'create'])

# 6. jail completely installed
   log(0, "new jail '%s' was successful created!" % newjail['name'], 1)

def linked_jail_fstab(link_path, jail_path):
    """
########################################################################################
#  create linked jail fstab
    """
# create linked jail fstab file
    os.system('mkdir -p %s/etc' % jail_path)
    os.system('echo \"%s %s unionfs rw,below 0 0\" > %s/etc/fstab' % (link_path, jail_path, jail_path))

def base_jail_fstab(base_path, jail_path):
    """
########################################################################################
#  create base jail fstab
    """
# fix jail rw/tmp
    os.system('chmod 1777 %s/rw/tmp/' % jail_path)
# create sekl jail fstab file
    os.system('echo \"%s %s/mnt nullfs ro 0 0\" > %s/rw/etc/fstab' % (base_path, jail_path, jail_path))
    os.system('echo \"%s/rw %s/mnt/SROOT nullfs rw 0 0\" >> %s/rw/etc/fstab' % (jail_path, jail_path, jail_path))

def modify_jail(newjail):
   """
########################################################################################
#  modify existing jail
#  newjail['modify'] - this is the original jail name
#
   """
   jails = load_jail_config(jailconf)[0]
# find modify jail
   for i in jails:
       if i['name'] == newjail['modify']:
           modjail = i
           break
# find BASE jail
   for i in jails:
       if i['name'] == 'BASE':
           base = i
           break

# if this jail i running
   if modjail['active'] == 1:
       log(2, "jail '%s' is running!" % modjail['name'])
       return False

# if this is zfs and we changeit
   move_zfs_path = 0
   if modjail['zfs'] != 0:
      move_zfs_path = 1
      zfs_path = modjail['zfs'][0]

      if newjail['zfs'][0] != zfs_path:
         subprocess.call(['zfs', 'rename', '%s' % zfs_path, '%s' % newjail['zfs'][0]])
         zfs_path = newjail['zfs'][0]
         log(0 ,"jail zfs '%s' was rename to %s" % (modjail['zfs'][0], newjail['zfs'][0]))

      if newjail['path'] != modjail['path']:
         subprocess.call(['zfs', 'set', 'mountpoint=%s' % newjail['path'], '%s' %  zfs_path])
         subprocess.call(['rm', '-rf', '%s' % modjail['path']])
         log(0, "jail path '%s' was modified to '%s'!" % (modjail['path'], newjail['path']))

      if newjail['zfs'][-2] != modjail['zfs'][-2]:
          subprocess.call(['zfs', 'set', 'quota=%s' % newjail['zfs'][-2], '%s' % newjail['zfs'][0]])
          log(0 ,"jail zfs quota '%s' was set for '%s'" % (newjail['zfs'][-2], newjail['zfs'][0]))

# if we change the path
   if move_zfs_path == 0:
      if newjail['path'] != modjail['path']:
         subprocess.call(['mkdir', '-p', newjail['path']])
         subprocess.call(['rm', '-rf', newjail['path']]) # this is not a mistake we want to remove last folder from newjail['path']
         subprocess.call(['mv', '-f', modjail['path'], newjail['path']])
         log(0, "jail path '%s' was modified to '%s'!" % (modjail['path'], newjail['path']))

# if we modify skeleton jail
   if 'SKEL' in newjail.keys():
# fix fstab
      base_jail_fstab(base['path'], newjail['path'])
      newjail['install'][0] = 'skel'

# update jail.conf
   update_jail_conf(['new_jail', [newjail, modjail], 'modify'])

# jail completely modified
   log(0, "jail '%s' was successful modified!" % newjail['name'], 1)

def import_jail(jpath):
   """
########################################################################################
#  import existing jail
#  check if japth folder exist and if we have /jpath/etc/jail.conf
   """
   jails = load_jail_config(jailconf)[0]
   jpath = ospath(jpath)
   if os.path.isdir(jpath) == False:
       log(2, "'%s' not valid path!" % jpath)
       return False

   i_jconf = '%s/etc/jail.conf' % jpath
   if os.path.isdir('%s/rw' % jpath) == True:
       i_jconf = '%s/rw/etc/jail.conf' % jpath

   if os.path.isfile(i_jconf) == False:
        log(2, "'%s/etc/jail.conf' cannot be found!" % jpath)
        return False

   i_jail = load_jail_config(i_jconf)[0][0]
   i_jail['install'] = ['n/a']
   if os.path.isdir('%s/rw' % jpath) == True:
       i_jail['install'] = ['skel']
# fix BASE jail fstab file
       base = None
       for i in jails:
          if i['name'] == 'BASE':
             base = i
             break
       if base == None:
          log(2, "'BASE' jail not exist!")
          return False
# create sekl jail fstab file
       base_jail_fstab(base['path'], jpath)

   i_jail['path'] = jpath

   for x in jails:
       for y in ['name', 'jid', '$jip', 'path']:
          if x[y] == i_jail[y]:
             log(2, "jail with %s '%s' already exist (jail: %s) !" % (y, i_jail[y], x['name']))
             return False

   if 'vnet' not in i_jail.keys():
       i_jail['vnet'] = 0

   update_jail_conf(['new_jail', [i_jail], 'create'])
   log(0, "jail '%s' was successful imported!" % i_jail['name'], 1)

def remote_nfs_jail(rpath, jpath):
   """
########################################################################################
#  add remote nfs jail
#
   """
   jails = load_jail_config(jailconf)[0]
   jpath = ospath(jpath)
   brpath = "None"
   dt = str(datetime.now())

# create temp nfs mount folder
   tmp_nfs = '/tmp/jadm_nfs_%s' % rpath.replace(':', '')
   nfshost = rpath.split(':')

   try:
      log(0, "create temp folder '%s'" % tmp_nfs)
      subprocess.call(['mkdir', '-p','%s' % tmp_nfs])
   except:
      log(1, "'%s' already exist" % tmp_nfs)
      pass
# try to mount remote path
   try:
      log(0, "try to mount '%s' in '%s'" % (rpath, tmp_nfs))
      subprocess.call(['mount_nfs', rpath, tmp_nfs])
   except:
      log(2, "'%s' cannot be mounted in '%s'" % (rpath, tmp_nfs))
      log(1, "remove '%s'" % (tmp_nfs))
      return False

# try to find local jail.conf
   i_jconf = '%s/etc/jail.conf' % tmp_nfs
   if os.path.isdir('%s/rw' % tmp_nfs) == True:
       i_jconf = '%s/rw/etc/jail.conf' % tmp_nfs

   if os.path.isfile(i_jconf) == False:
        log(2, "'%s/etc/jail.conf' cannot be found!" % tmp_nfs)
        return False

# CREATE REMOTE JAIL
   i_jail = load_jail_config(i_jconf)[0][0]
   i_jail['install'] = ['nfs']
   i_jail['path'] = "%s/%s/%s" % (jpath, nfshost[0], i_jail['name'])

# check if remote jail already exist on localhost
  # for x in jails:
  #     for y in ['name', 'jid', '$jip', 'path']:
  #        if x[y] == i_jail[y]:
  #           log(2, "jail with %s '%s' already exist (jail: %s) !" % (y, i_jail[y], x['name']))
  #           return False

   if 'vnet' not in i_jail.keys():
       i_jail['vnet'] = 0

   print i_jail

# create folders
   try:
      subprocess.call(['mkdir', '-p', "%s/mnt" % i_jail['path']])
      subprocess.call(['mkdir', '-p', "%s/%s/%s/rw/etc/" % (jpath, nfshost[0], i_jail['name'])])
      log(0, "jadm create %s/%s/%s/{rw/etc/, mnt/} folders" % (jpath, nfshost[0], i_jail['name']))
   except:
      log(2, "jadm cannot create %s/%s/%s/{rw/etc/, mnt/} folders" % (jpath, nfshost[0], i_jail['name']))
      return False

# create remote jail fstab file
   os.system('echo \"%s %s/mnt %s\" > %s/%s/%s/rw/etc/fstab' % (rpath, i_jail['path'], remnfs, jpath, nfshost[0], i_jail['name']))
   log(0, "%s/%s/%s/rw/etc/fstab was created" % (jpath, nfshost[0], i_jail['name']))
# create remote info file
   os.system('echo \"nfs location: %s \" > %s/%s/%s/remote.info.jadm' % (rpath, jpath, nfshost[0], i_jail['name']))
   os.system('echo \"nfs BASE:     %s \" >> %s/%s/%s/remote.info.jadm' % (brpath, jpath, nfshost[0], i_jail['name']))
   os.system('echo \"date added:   %s \" >> %s/%s/%s/remote.info.jadm' % (dt, jpath, nfshost[0], i_jail['name']))

      # os.system('echo \"%s/rw %s/mnt/SROOT nullfs rw 0 0\" >> %s/etc/fstab' % (rpath, jpath, i_jail['path']))

   update_jail_conf(['new_jail', [i_jail], 'create'])
   log(0, "jail '%s' was successful imported from '%s'!" % (i_jail['name'], rpath), 1)

# unmount and remove tempoary folder
   try:
      log(0, "unmount '%s' and remove '%s'" % (rpath, tmp_nfs))
      subprocess.call(['umount', tmp_nfs])
      return True
   except:
      log(2, "cannot unmount '%s' and remove '%s'" % (rpath, tmp_nfs))
      return False

def jail_destroy(jailname, action = None, base = None):
   """
########################################################################################
#  Remove / Destroy existing jail
#
   """
   jails = load_jail_config(jailconf)[0]

# find jail
   rmjail = check_jail_Setting('modify', jailname)
   if rmjail == False:
       return False
   if base != None:
      log(1, "jadm will clear '/usr/freebsd-dist/' folder")
   if autoc != 'y':
      yn = raw_input("will %s '%s' jail please confirm (y):> " % (action, rmjail['name']))
   else:
      yn = 'y'

   if 'y' not in yn:
      log(0, 'user exit - !')
      return False

   if base != None:
      os.system('rm -rf /usr/freebsd-dist/*')
      log(1, "jadm clear '/usr/freebsd-dist/' folder", 1)

   if action == 'remove':
       update_jail_conf(['rm_jail', rmjail])

   if action == 'destroy':
       try:
          if rmjail['zfs'] != 0:
              subprocess.call(['zfs', 'destroy', '-r', rmjail['zfs'][0]])

          subprocess.call(['chflags', '-R', '0', rmjail['path']])
          subprocess.call(['rm', '-rf', '%s' % rmjail['path']])
          update_jail_conf(['rm_jail', rmjail])
       except:
          log(2, "jadm was not %s '%s' jail successful!" % (action, rmjail['name']), 1)
          return False

   log(1, "jadm %s '%s' jail successful!" % (action, rmjail['name']), 1)
   return True

def srcbuild():
   """
########################################################################################
# build OS sources
#
   """
   try:
      os.chdir("/usr/src/")
      subprocess.call(['make', 'buildworld'])
      log(0, "OS sources are build successful!", 1)
      return True
   except:
      log(2, "jadm was not able to build OS sources successful!", 1)
      return False

def BASE(src, path = None):
   """
########################################################################################
# create and update BASE jail used for skeleton moedl
#
   """
   if src not in ['bsd', 'src', 'ports', 'sources']:
       log(2, "'%s' is not a valid 'create BASE' option" % src)
       return False

   jails = load_jail_config(jailconf)[0]
# check FreeBSD version
   hwplatform = subprocess.check_output(['uname', '-m']).strip('\n')
   release =  subprocess.check_output(['uname', '-r'])[:4].strip('\n')

# find base path
   BASE = check_jail_Setting('modify', 'BASE')

# BASE ports
   if src == 'ports':
      if BASE == False:
         log(2, "'BASE' jail not exist!")
         return False
      if path == 'install':
         subprocess.call(['portsnap' ,'-p', '%s/usr/ports' % BASE['path'], 'fetch', 'extract'])
         log(0, "'BASE' ports tree was istalled!")
         return True
      if path == 'update':
         subprocess.call(['portsnap' ,'-p', '%s/usr/ports' % BASE['path'], 'fetch', 'update'])
         log(0, "'BASE' ports tree was updated!")
         return True

# CHECK IF BASE exist before install
   if BASE != False:
      log(2, "'BASE' jail already exist!")
      return False

# INSTALL BASE JAIL
# check new BASE path
   if '/' not in path:
      log(2, "'%s' is not a valid path!" % path)
      return False

   if path[-1] == '/':
       path = path[:-1]

   path = ospath('%s/BASE' % path)
# if folder exist exit
   if os.path.isdir('%s' % path) == True:
      log(2, "'%s' already exists!" % path)
      return False

# install from bsd.txz files
   if src == 'bsd':
# needed files
      sources = ['base.txz']
      if hwplatform == 'amd64':
         sources.append('lib32.txz')
# check if we have all needed source files
      if os.path.exists("/usr/freebsd-dist") == False:
          subprocess.call(['mkdir', '-p','/usr/freebsd-dist'])

# downlaod BASE files
      for s in sources:
         if os.path.exists("/usr/freebsd-dist/%s" % s) == False:
            log(2, "'%s' cannot be found!" % s)
# try to download freebsd src file if not exist
            try:
               log(0, "downloading '%s' from 'ftp.freebsd.org' via http!" % s)
               urllib.urlretrieve ("http://ftp.freebsd.org/pub/FreeBSD/releases/%s/%s-RELEASE/%s" % (hwplatform, release, s), "/usr/freebsd-dist/%s" % s)
               log(0, "'%s' downloaded!" % s)
            except:
               log(2, "'%s' cannot be downloaded!" % s)
               return False

# create BASE folder befire install
   subprocess.call(['mkdir', '-p', '%s' % path])
   log(0, "'%s' was created!" % path)

# extract freebsd src file
   if src == 'bsd':
       for s in sources:
          subprocess.call(['tar', '-xf', '/usr/freebsd-dist/%s' % s, '-C', path])
          log(0, "'%s' was successful extracted in '%s'!" % (s, path))

   if src == 'src':
      try:
         os.chdir("/usr/src/")
         subprocess.call(['make', 'installworld', 'DESTDIR=%s' % path])
         subprocess.call(['make', 'distribution', 'DESTDIR=%s' % path])
      except:
         log(2, "'BASE' cannot be installed from 'src'!")
         return False

# create ports tree dir
   subprocess.call(['mkdir', '-p', '%s/usr/ports' % path])
# create SKELETON MODEL
# http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/jails-application.html
   log(0, 'create SROOT and symlinks!')
   os.chdir('%s' % path)
   os.system('mkdir -p %s/SROOT/home %s/SROOT/usr-X11R6 %s/SROOT/distfiles %s/SROOT/usr-share-keys %s/SROOT/compat' % (path,  path, path, path, path))
   os.system('mv %s/etc SROOT/' % path)
   os.system('mv %s/usr/local SROOT/usr-local' % path)
   os.system('mv %s/usr/share/keys/* SROOT/usr-share-keys/' % path)
   os.system('rm -rf %s/usr/share/keys/' % path)
   os.system('mv %s/tmp SROOT/' % path)
   os.system('chmod 1777 SROOT/tmp')
   os.system('mv %s/var SROOT/' % path)
   os.system('mv %s/root SROOT/' % path)
   os.system('ln -s SROOT/etc etc')
   os.system('ln -s SROOT/home home')
   os.system('ln -s SROOT/root root')
   os.system('ln -s /SROOT/usr-local usr/local')
   os.system('ln -s /SROOT/usr-share-keys usr/share/keys')
   os.system('ln -s /SROOT/usr-X11R6 usr/X11R6')
   os.system('ln -s /SROOT/distfiles usr/ports/distfiles')
   os.system('ln -s SROOT/tmp tmp')
   os.system('chmod 1777 %s/SROOT/tmp/' % path)
   os.system('ln -s SROOT/var var')
   os.system('ln -s SROOT/compat compat')
# Create a generic /home/j/skel/etc/make.conf containing this line
   os.system('echo  \"WRKDIRPREFIX?=  /SROOT/portbuild\"  > %s/etc/make.conf' % path)

# update jail.conf for BASE jail
   basejail = {'name':'BASE', 'jid':0, '$jip':'n/a', '$jgw':'n/a', 'host.hostname':'n/a', 'vnet':0, 'install':'bsd', 'path':path}
   update_jail_conf(['new_jail', [basejail], 'create'])

   log(0, "'BASE' jail was successful installed from '%s'!" % src)
   return True

def cli():
   """
########################################################################################
#  jadm command line interface
#
   """

   parser = argparse.ArgumentParser(prog='jadm')
   parser.add_argument('-s','--start',nargs='*', help='start jail')
   parser.add_argument('-k','--stop', nargs='*', help='stop jail')
   parser.add_argument('-sk','--reboot',nargs='*', help='reboot jail')
   parser.add_argument('-sh','--shell', nargs='*', help='shell login to jail')
   parser.add_argument('-l','--list', help='list available jails', action='store_true')
   parser.add_argument('-sl','--jls', help='short list available jails', action='store_true')
   parser.add_argument('-sn','--snap', nargs='*', help='zfs jails snaphosts')
   parser.add_argument('-ar','--archive', nargs='*', help='jails tar backup')
   parser.add_argument('-g','--gateways', help='list bridge gateways', action='store_true')
   parser.add_argument('-jl','--locals', nargs='*', help='jail local settings')
   parser.add_argument('-jg','--globals', nargs='*', help="jails global settings .. add settings with ''")
   parser.add_argument('-d','--destroy', nargs='*', help='destroy jail')
   parser.add_argument('-r','--remove', nargs='*', help='remove jai;')
   parser.add_argument('-nm','--migrate', nargs='*', help='migrate jail to netwrok server')
   parser.add_argument('-ns','--server', nargs='*', help='jadm network server')
   parser.add_argument('-c', '--create', nargs='*', help='create new jail')
   parser.add_argument('-m', '--modify', nargs='*', help='modify jail')
   parser.add_argument('-i', '--info', help='sytem info', action='store_true')
   parser.add_argument('-y', '--yes', help='auto confirm', action='store_true')
   parser.add_argument('-v', '--verbose', help='jail start/stop verbose output', action='store_true')
   parser.add_argument('-u', '--update', help='try to make /etc/jail.conf autoupdate (experimental)', action='store_true')
   parser.add_argument('-a', '--about', help='about jadm', action='store_true')
   parser.add_argument('-vh', '--vhelp', help='verbose help')
   args = parser.parse_args()
   p = vars(args)

   for i in p.keys():
      p[i.strip('-')] = p.pop(i)

   if p['verbose'] == True:
      global verbose
      verbose = 1
      log(0, "run/stop jail verbose mode enable!")
   if p['yes'] == True:
      global autoc
      autoc = 'y'
      log(1, "auto confirm mode enable!")
   if p['info'] == True:
      welcome()
   if p['about'] == True:
      lcmd().onecmd('about')
   if p['vhelp'] != None:
      lcmd().onecmd('help %s' % p['vhelp'])
# -update jail.conf in jadm format
   if p['update'] == True:
      fix_jailconf(jailconf)
      sys.exit(0)
   if p['list'] != False:
      lcmd().onecmd('list')
   if p['jls'] != False:
      lcmd().onecmd('jls')
   if p['gateways'] != False:
      lcmd().onecmd('gateways')
# cli create or modify jail
   if p['create'] != None and p['modify'] != None:
      log(2, "(c)reate and (m)odify cannot be used together!")
      sys.exit(2)
   elif p['create'] != None:
      createcli = ['-c']
      createcli.extend(p['create'])
      jail_input_cli(createcli)
   elif p['modify'] != None:
      modcli = ['-m']
      modcli.extend(p['modify'])
      jail_input_cli(modcli)

   for i in ['verbose', 'yes', 'info', 'about', 'vhelp', 'update', 'list', 'jls', 'gateways', 'create', 'modify']:
      p.pop(i)
   for i in p.keys():
       if p[i] != None:
          jcmd = ' '.join(p[i])
          lcmd().onecmd("%s %s" % (i, jcmd))

def jail_input_cli(cliargv):
   """
########################################################################################
#  jadm command line interface input arguments for create_jail() and modify_jail()
#
   """
   load_jails = load_jail_config(jailconf)
   jails, gsettings = load_jails[0], load_jails[1]
   new_jail = {'name':'', 'host.hostname':'', 'jid':'', '$jip':'',
           '$jgw':'system','path':'', 'zfs':['none', 'none', 'none'],
           'vnet':0, 'gwnetmask':''}
   modjail = {'name':'', 'host.hostname':'', 'jid':'', '$jip':'',
               '$jgw':'','path':'', 'zfs':['', '', ''] }
   jail = {}
   input_keys = ['name', 'id', 'ip', 'path', 'install']

# pars all sys_argv arguments to jail config
   for i in cliargv:
       i = i.split(':')
       if len(i) > 1:
           jail[i[0]] = i[1:]

# check for existing jail name/id
   if 'id' in jail.keys() and 'name' not in jail.keys():
     if len(jail['id']) > 2:
        log(2, "jail id was not specified correctly!")
        sys.exit(2)

     try:
        int(jail['id'][0])
     except:
        log(2, "jail id was not specified correctly!")
        sys.exit(2)

# check if jail exist
     isjail = check_jail_Setting('modify', jail['id'][0])
     if isjail == False:
        sys.exit(2)
     else:
        if isjail['name'] == 'BASE':
           log(2, "'BASE' jail can't be modified!")
           sys.exit(2)
     if 'name' not in jail.keys() or len(jail['name']) > 2:
        jail['name'] = [isjail['name']]
# replace id if we change it
     if len(jail['id']) == 2:
         jail['id'] = [jail['id'][1]]

   if 'name' not in jail.keys() or len(jail['name']) > 2:
      log(2, "jail name was not specified correctly!")
      sys.exit(2)

# check if we have all needed arguments to create jail
   if '-c' in cliargv:
       for i in input_keys:
           if i not in jail.keys():
              log(2, "'%s' not in jail config!" % i)
              sys.exit(2)

# check if we have all needed arguments to modify jail
   if '-m' in cliargv:
# chceck if modify jail exist
       modjail = check_jail_Setting('modify', jail['name'][0])
       if modjail == False:
          sys.exit(2)

# fix if is not vnet
       if 'vnet' not in modjail.keys():
          modjail['vnet'] = 0
# fix if is not 'zfs'
       if modjail['zfs'] == 0:
          modjail['zfs'] = ['none', 'none', 'none']
#add empty 'install'
       modjail['install'] = ['', '']
       new_jail = copy.deepcopy(modjail)

# check jail name
   for i in jail.keys():
      if i == 'name':
         if len(jail['name']) == 2 and '-c' not in cliargv:
            new_jail['name'] = jail['name'][1]
            if new_jail['name'] == modjail['name']:
                break
         elif len(jail['name']) == 1:
            if '-m' in cliargv:
               new_jail['name'] = modjail['name']
               break
            else:
               new_jail['name'] = jail['name'][0]
         else:
            log(2, "jail name/jid was not specified correctly!")
            sys.exit(2)

         if check_jail_Setting(i, new_jail[i]) == False:
             sys.exit(2)
      else:
         if i == 'install':
             pass
         elif i == 'zfs':
             pass
         else:
             jail[i] = jail[i][0]

# check jail id 'jid'
      if i == 'id':
         if '-m' in cliargv:
            if jail['id'] == modjail['jid']:
               break
         if check_jail_Setting(i, jail['id']) == False:
            sys.exit(2)
         else:
            new_jail['jid'] = jail['id']

# check if jail is vnet and gateway
      if i == 'vnet':
          if '-m' in cliargv:
             if jail[i] == modjail['$jgw']:
                break
          gws = gateways()
          find_gw = check_jail_Setting(i, [jail[i], gws])
          if find_gw == False:
             gateways('table')
             sys.exit(2)
          else:
             new_jail['$jgw'] = find_gw[0]
             new_jail['$bridge'] = find_gw[1]
             new_jail['gwnetmask']= find_gw[2]
             new_jail['vnet'] = 1

# check jail ip address
      if i == 'ip':
          if '-m' in cliargv:
             if jail[i] == modjail['$jip']:
                break
          if jail['ip'] != 'dhcp':
             ip = check_jail_Setting(i, [jail['ip'], new_jail, modjail])
             if ip == False:
                sys.exit(2)
             else:
                if ip == modjail['$jip'][:-3]:
                   new_jail['$jip'] = '%s%s' % (ip, modjail['$jip'][-3:])
                else:
                   new_jail['$jip'] = ip
          else:
             new_jail['$jip'] = 'dhcp'

# check jail hostname
      if i == 'hostname':
          if '-m' in cliargv:
             if jail[i] == modjail['host.hostname']:
                break
          if check_jail_Setting(i, jail[i]) == False:
              pass
          else:
              new_jail['host.hostname'] = jail[i]

# check jail path
      if i == 'path':
          jpath = "%s/%s" % (jail[i], new_jail['name'])
          if '-m' in cliargv:
             if jpath == modjail['path']:
                break
          if check_jail_Setting(i, jpath) == False:
              sys.exit(2)
          else:
              new_jail['path'] = jpath

# check jail zfs
      if i == 'zfs':
          if '-m' in cliargv:
             if modjail['zfs'] == 0:
                 break
             if jail[i] == modjail['zfs']:
                break
             if jail[i][0] == 'quota':
                if  check_jail_Setting('zquota', jail[i][1]) == False:
                   sys.exit(2)
                else:
                   new_jail['zfs'][-2] = jail[i][1]
                   break

          ezfs_check = check_jail_Setting('zfs', [jail[i][0], new_jail['name']])
          if ezfs_check == False:
             sys.exit(2)
          else:
             new_jail['zfs'][0] = '%s/%s' % (jail[i][0], new_jail['name'])
# check zfs quota
          if len(jail[i]) == 2:
             if  check_jail_Setting('zquota', jail[i][1]) == False:
                sys.exit(2)
             else:
                new_jail['zfs'][-2] = jail[i][1]
                break

# check install src
      if i == 'install' and '-m' not in cliargv:
         if jail[i][0] != 'template' and len(jail[i]) == 1:
            if check_jail_Setting('install src', jail[i][0]) == False:
               sys.exit(2)
            else:
               new_jail['install'] = jail[i]

         elif jail[i][0] == 'template' and len(jail[i]) == 2:
            template =  check_jail_Setting('template', jail[i][1])
            if template == False:
               sys.exit(2)
            else:
               new_jail['install'] = jail[i]
         else:
            log(2, "jail install source was not specified correctly!")
            sys.exit(2)

# debug print :)
#   print jail
#   print modjail
#   print new_jail
#
# check if we have changes:
   if '-m' in cliargv:
      modjail_diff = 0
      for i in modjail.keys():
         if new_jail[i] != modjail[i]:
            modjail_diff = 1
      if modjail_diff == 0:
            log(0, "no changes in '%s' settings" % modjail['name'])
            sys.exit(2)
      new_jail['modify'] = modjail['name']
      modify_jail(new_jail)

# proceede new_jail
   if '-c' in cliargv:
      create_jail(new_jail)

def welcome():
   '''
########################################################################################
#       print welcome info on startup
#
   '''

   jails = load_jail_config(jailconf)[0]

   welmsg = '''
FreeBSD jail framework with vnet, zfs and jail.conf support.
Please check license agreement with "about"
List available commands with "help" or detailed help with "help cmd".
   '''

   lmenu = ['FreeBSD', 'Hostname', 'Uptime', 'Jails', 'Active',  'JADM']

   hostname = os.uname()[1]
   opsys = os.uname()[2]
# check how many jails we have
# remove BASE jail if exist
   jailNumbers = len(jails)
   act = []
   for i in jails:
       if 'BASE' == i['name']:
          jailNumbers = jailNumbers - 1
# check actvie jails number
       if i['active'] == 1:
        act.append(i['name'])
   activeJails =  len(act)
# check system uptime
   proc = subprocess.Popen('uptime', stdout=subprocess.PIPE)
   output = proc.stdout.read()
   uptime = output.split(',')[0]
   uptime = uptime.split('up')[1]
   hostInfo = [[opsys,  hostname, uptime, jailNumbers, activeJails,  jadm_version]]
   print ""
   print "Welcome to JADM (jail admin) framework"
   print ""
   print tabulate(hostInfo, lmenu)
   print welmsg

   if jailNumbers <= 0:
      log(2, "jadm cannot find any jail in '%s'" % jailconf, 1)
      log(0, "you can try to format '%s' with 'jadm -update' command from os shell!" % jailconf)
      log(0, "each jail in '%s' should be at leas in this format!" % jailconf)
      print " \njailname \
        \n{ \
        \n...; \
        \n} \n"

def about():
   """
########################################################################################
# Shouw creator information also jadm license
#
   """
# return about string
   about = ("""
JADM %s
-------------
creator:  Nikolay Georgiev Dachev, <nikolay@dachev.info>
support:  jadm@dachev.info (only for bugs report and jadm issues)

Jadm is FreeBSD jail administration framework with jail.conf, vnet and zfs support.

---------------- JADM is BSD 3-Clause Licensed ---------------------

Copyright (c) <2015>, <Nikolay Georgiev Dachev> <nikolay@dachev.info>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distr

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
""") % (jadm_version)
   return about

def sAction(s,  j = None):
   """
########################################################################################
# r - stop jail
# c - start jail
# rc  - recreate
   """
   act_jail = {'c':'start', 'r':'stop', 'rc':'reboot'}
   if verbose == 0:
      subprocess.Popen("jail -%s %s" % (s, j), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).wait()
   else:
      subprocess.Popen("jail -v -%s %s" % (s, j), shell=True).wait()
   log(0, "'%s' was '%s'!" % (j, act_jail[s]), 1)

def startstop_jail(jnid = None, action = None):
   """
########################################################################################
#  start/stop single or all jails
#
   """
   jails = load_jail_config(jailconf)[0]

   ssjail = []
# add all jails
   if jnid == 'all':
      for i in jails:
         if i['name'] != 'BASE':
            ssjail.append(i['name'])
   else:
# check if jail exist
      isjail = check_jail_Setting('modify', jnid)
      if isjail == False:
         return False
      else:
          if isjail['name'] == 'BASE':
              return False
          ssjail = [isjail['name']]

# do action
   for i in ssjail:
      jail = {}
      for x in jails:
         if x['name'] == i:
             jail = x.copy()
             break

      if jail['active'] == 'dying':
         log(1, "'%s' is dying and cannot be '%s'!" % (jail['name'], action))
         continue
      try:
# start jail

         if action == 'start':
            if jail['active'] == 1:
               log(1, "'%s' is already started!" % (jail['name']))
               continue
            else:
               sAction('c',  jail['name'])
# stop jail
         elif action == 'stop':
            if jail['active'] == 0:
               log(1, "'%s' already stopped!" % (jail['name']))
               continue
            else:
               sAction('r',  jail['name'])

# reboot jail
         elif action == 'reboot':
            if jail['active'] == 1:
               sAction('r',  jail['name'])
            sAction('c',  jail['name'])
         else:
             return False

      except:
         log(2, "jadm cannot perform '%s' action for '%s'!" % (action, i))

   return True

def jail_shell(jnid = None):
   """
########################################################################################
# enter in jail shell (jexec) using root shell from jail /etc/passwd
#
   """
   jails = load_jail_config(jailconf)[0]

# check if jail exist
   j = check_jail_Setting('modify', jnid)
   if j == False:
      return False

   if j['active'] !=  1:
       log(2, "'%s' is not running!" % j['name'])
       return False

# find jail root shell!
   if 'SKEL' in j.keys():
      root_sheel = subprocess.check_output("head -n3 %s/mnt/etc/passwd" % j['path'], shell=True).strip('jid')
   else:
      root_sheel = subprocess.check_output("head -n3 %s/etc/passwd" % j['path'], shell=True).strip('jid')
   root_sheel = root_sheel.split(':')

   log(0, "Enter in '%s' jail!" % j['name'])
   try:
       os.system ("jexec %s %s" % (j['name'], root_sheel[-1]))
   except:
       log(0, "can't login in '%s'!" % j['name'])
       return False

   log(0, "Exit from '%s' jail!" % j['name'])

def fix_jailconf(conf_f):
   """
########################################################################################
#  try to pars jail.conf to format readable from jadm
#
   """

   format = "%H_%M_%S"
   dt = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')

# create backup
   bckjailconf = '%s.backup.%s' % (conf_f, dt)
   subprocess.call(['cp', conf_f, bckjailconf])
   log(0, "'%s' backup was created!" % bckjailconf, 1)

# open jail.conf
   jc = open(conf_f, 'r')
# get all elements
   mas = []
   jails = []
   for i in jc:
      i = i.strip('\n')
      i = i.strip('\t')
      mas.append(i)
   jc.close()

# some default vars
   bridge1 = "vnet bridge"
   jgateway = "vnet gateway"
   path1 = False

# check if this is jadm 1 file
   jadm1 = ['$jzfs',  '$jedir', '$bridge', '$epair']
   test_jc = []
   isjadm1 = 0
   fixBASE = 0
   for x in mas:
       for y in jadm1:
           if y in x:
              test_jc.append(y)
              if y == '$jedir':
                  path1 = x.strip(y).replace('"', '').replace(';', '').replace('=', '').replace(' ', '')
              if y == '$bridge':
                  bridge1 = x.strip(y).replace('"', '').replace(';', '').replace('=', '').replace(' ', '')
              break
       if test_jc == jadm1:
           if path1 == False:
               log(2, "jail.conf is JADM ver 1.x file but 'path' was not found!")
               sys.exit(2)
           log(0, "'%s' is JADM ver 1.x: path = '%s' : $bridge = '%s'" % (conf_f, path1, bridge1))
           isjadm1 = 1
           break

# find jails
   enmas = enumerate(mas)
   foundjails = []
   fj = []
   fixed_jail_conf = []
# fix { } + \n
   for x,y in enmas:
# jail begin
       if "{" in y and "$" not in y:
           if y != "{":
               y = y.split(' ')
               y = cleanlist(y)
               for a,b in enumerate(y):
                  if b == '{':
                     jname =  "%s" % (''.join(y[:a]))
                     jails.append(jname)
                     jstart =  '{'
                     jother =  "%s" % (''.join(y[a+1:]))
               mas[x] = jname
               mas.insert(x+1, jstart)
               mas.insert(x+2, jother)
               js = x
           if y == "{":
               jname = mas[x-1]
               js = x-1
# add jail name/begin to temp list
           fj.append(jname)
           fj.append(js)

# jail end
       if "}" in y and "$" not in y:
           if y != "}":
               y = y.strip('\n')
               y = y.strip('\t')
               y = y.split(' ')
               y = cleanlist(y)
               for a,b in enumerate(y):
                  if b == '}':
                     before =  "%s" % (''.join(y[:a]))
                     jend =  '}'
                     jother =  "%s" % (''.join(y[a+1:]))
               mas[x] = before
               mas.insert(x+1, jend)
               mas.insert(x+2, jother)

# add jail to temp list
           fj.append(x)
           foundjails.append(fj)
           fj = []

# append golabl vars to jail.conf
   basejails = []
   if isjadm1 == 0:
      fixed_jail_conf = mas[:foundjails[0][1]]
   else:
# check for BASE-jails before we continue
       for i in foundjails:
           if isjadm1 == 1:
              if 'BASE-' in i[0]:
                 basejails.append([i[0]])

# confirm if we will fix BASE- ZFS auto.
   if basejails:
      log(1, "jadm find %s BASE- jails created by JADM ver 1.x '!" % len(basejails))
      print tabulate(basejails, ['jadm ver.1.x BASE- jails'])
      if autoc != 'y':
         log(1, "jadm can try to convert BASE-jails ZFS structure to new skeleton model")
         log(1, "jadm will move jail ZFS mount point - 'wr' data from %sBASE-RW/jailname/ to %s/jailname/rw/" % (path1, path1))
         log(1, "also jadm will fix and BASE-jails fstab file.")
         yn = raw_input("please confirm for all jails (yes):> ")
         if yn == 'yes':
            log(1, "jadm will fix BASE-jails ZFS structure to new skeleton model automatically !", 1)
            fixBASE = 1
         else:
            log(1, "jadm will NOT fix BASE-jails ZFS structure please do this manually!", 1)
      else:
         log(1, "jadm will fix BASE-jails ZFS structure to new skeleton model automatically !", 1)
         fixBASE = 1

   vnetjails = []
   jid = 1
# try to fix jail settings
   for i in foundjails:
      index = 0
      isvnet = 0
      jail = mas[i[1]:i[2]]
      new_jail_locals = []
# try to fix path and path1
      if path1 == False:
         for i in jail:
             if 'path' in i:
                 i.replace('"', '').replace(';','')
                 i = i.split('=')
                 path1 = i[1]

# 0. Fix BASE
      if i[0] == 'BASE':
          log(0, "'BASE' jail found!", 1)
          new_jail = [' ',
                      'BASE',
                      '{',
                      '# %s ' % dt,
                      'jid = 0;',
                      '$jip = "n/a";',
                      '$jgw = "n/a";',
                      'host.hostname = "n/a";',
                      'path = "%sBASE";' % path1,
                      '### network settings ###',
                      'ip4.addr = "$jip";',
                      'exec.start = "/bin/sh /etc/rc";',
                      'exec.stop = "/bin/sh /etc/rc.shutdown";',
                      'persist;',
                      '#@# local settings #@#',
                      '}']
# fix SROOT
          try:
              subprocess.call(['cp', '-f', '-R', '%sBASE-SKELETON/' % path1, '%sBASE/SROOT/' % path1])
              log(0, "'BASE/SROOT' was fixed!", 1)
          except:
              log(2, "'BASE/SROOT' was not fixed please check it manually!", 1)

# append BASE jail config to jail.conf
          for a in new_jail:
             fixed_jail_conf.append(a)
          continue

# 1. add jail name, #dt and jid
      new_jail = [' ', i[0],
                 '{',
                 '# %s ' % dt,
                 'jid = %s;' % jid]
# update jid
      jid += 1

# search jail
      lscheck = 0
      for y in jail:

# append local settings
         if lscheck == 1:
             if '}' not in y:
                new_jail_locals.append(y)
                continue
             else:
                lscheck = 0
                continue

# try to find local settings
         if y == '### local settings ###':
            lsheck = 1
            continue

# 2. add jip
         if isjadm1 == 0:
            if "ip4.addr" in y:
               y = y.strip('\n')
               y = y.strip('\t')
               y = y.strip(';')
               y = y.split('=')
               y = cleanlist(y)
               for chip in y:
                  if ch_ipv4(chip) != False:
                     new_jail.append("$jip = %s;" % chip)
         else:
            if "$jip =" in y:
               new_jail.append("$jip = %s;" % y.strip("$jip").replace('"', '').replace(';', '').replace('=', '').replace(' ', ''))
# 3. find hostname
         if 'host.hostname' in y:
             new_jail.append('host.hostname = "%s";' % y.strip('host.hostname').replace('"', '').replace(';', '').replace('=', '').replace(' ', ''))
             continue
# 4. mark is as vnet jail.
         if "vnet" in y:
             isvnet = 1
             continue
# 5. path
         if "path" in y:
              new_jail.append('path = "%s";' % y.strip('path').replace('"', '').replace(';', '').replace('=', '').replace(' ', ''))
              continue
# 6. search for $jgw
         if "$jgw = " in y:
            jgateway = y.strip("$jgw").replace('"', '').replace(';', '').replace('=', '').replace(' ', '')
            continue

#7. add all other jail settings under local
         if isjadm1 == 0:
            if y not in ['{', '}', 'jid'] and y != i[0]:
               new_jail_locals.append(y)
               continue

# update vnet count index
         index += 1

# add path if is jadm ver 1
      if isjadm1 == 1:
         if 'BASE-' in i[0]:
            new_jail.append('path = "%s%s/mnt";' % (path1, mas[i[1]]))
         else:
            new_jail.append('path = "%s%s";' % (path1, mas[i[1]]))

# append jail network configuration
      if isvnet == 1:
          vnetjails.append([i[0]])
          new_jail.append('$jgw = "%s";' % jgateway)
          new_jail.append('$bridge = "%s";' % bridge1)
          for n in vnet:
             new_jail.append(n)
      else:
          jail.append('$jgw = "system";')
          for n in net:
              new_jail.append(n)

# check if this is BASE-jail form jadm v 1.0
      if isjadm1 == 1:
         if "BASE-" in i[0]:
            new_jail.extend(['### BASE mount settings ###', 'mount.fstab = %s%s/rw/etc/fstab;' % (path1, i[0]), 'mount.devfs;'])
# cehck if we jadm will fix ZFS for BASE-jail + fstab
            if fixBASE == 1:
                new_jail_zfs  = zfs('%sBASE-RW/%s' % (path1, i[0]))
                try:
                   os.system('zfs set mountpoint=%s%s %s' % (path1, i[0], new_jail_zfs[-1][0]))
                   os.system('mkdir -p %s%s/rw' % (path1, i[0]))
                   os.system('mv -f %s%s/* %s%s/rw/' * (path1, i[0], path1, i[0]))
                   os.system('mkdir -p %s%s/mnt' % (path1, i[0]))
                   log(0, "%s: new zfs mount point '%s%s' : '%s' " % (i[0], path1, i[0], new_jail_zfs[-1][0]), 1)
                except:
                   log(2, "%s: not abel to set new zfs mount point, please do this manually!" % (i[0]), 1)
                try:
# create sekl jail fstab file
                   os.system('echo \"%sBASE %s%s/mnt nullfs ro 0 0\" > %s%s/rw/etc/fstab' % (path1, path1, i[0], path1, i[0]))
                   os.system('echo \"%s%s/rw %s%s/mnt/SROOT nullfs rw 0 0\" >> %s%s/rw/etc/fstab' % (path1, i[0], path1, i[0], path1, i[0]))
                   log(0, "%s: '%s%s/rw/etc/fstab' was updated!" % (i[0], path1, i[0]), 1)
                except:
                   log(2, "%s: '%s%s/rw/etc/fstab' was not updated!" % (i[0], path1, i[0]), 1)

      new_jail.extend(['#@# local settings #@#'])
      print new_jail_locals
     # if new_jail_locals:
     #     for i in new_jail_locals:
     #         new_jail.append(i)
      new_jail.extend(['}', ' '])

# append jail confi to jail.conf
      for a in new_jail:
          fixed_jail_conf.append(a)

# write jail.conf
   jcl = cleanlist(fixed_jail_conf)
   jcl = [x for x in jcl if x not in [' ', '', '    ']]
   jc = open(conf_f, 'w')
   jc.write('# This file is managed by JADM %s\n' % jadm_version)
   jc.write('#\n')
   for i in jcl:
       jc.write("%s\n" % i)
       if i == '}':
          jc.write("\n")
   jc.close()

   log(0, "'%s' update was completed" % conf_f)
   if vnetjails:
      log(1, "jadm find %s vnet jails, please update their gateway manually with 'modify jid/name'!" % len(vnetjails))
      print tabulate(vnetjails, ['vnet jails'])
   log(0, "please check '%s' manualy" % conf_f)

class jail_zfs_snap:
   """
########################################################################################
#  list, create or remove zfs jail snapshots
#
   """
   jails = load_jail_config(jailconf)[0]

   def find_jail(self, jnid):
# check if jail exist
      self.jail = check_jail_Setting('modify', jnid)
      if self.jail == False:
         return False
# check if this is zfs jail
      if self.jail['zfs'] == 0:
         log(2, "'%s' is not a zfs jail!" % self.jail['name'])
         return False

   def list(self, jail, tab = 1):
# get zfs snapshots
      if self.find_jail(jail) == False:
          return False
      jzfs = []
      jzfsnum = 0
      zfs_snap_search = self.jail['zfs'][0]+'@'
      cmd = "zfs list -t snapshot |grep %s" %  (zfs_snap_search)
      try:
         for x in subprocess.check_output(cmd, shell=True).split('\n'):
            x = cleanlist(x.split(' '))
            jzfs.append([jzfsnum, x[0], x[3]])
            jzfsnum += 1
      except:
         pass
      if jzfs == []:
         log(0, "'%s' jail - '%s' doesnt have zfs snapshots!" % (self.jail['name'], self.jail['zfs'][0]))
         return False
      lmen = ["'%s'" % self.jail['name'],  " ZFS SNAPSHOTS", "SIZE"]
      if tab == 1:
         print tabulate(jzfs, lmen)
      return jzfs

   def create(self, jail):
# create jail zfs snapshot
      if self.find_jail(jail) == False:
          return False
      dt = str(datetime.utcnow().isoformat())
      jzsnap = "%s@%s" % (self.jail['zfs'][0], dt)
      try:
          os.system("zfs snapshot "+jzsnap)
          log(0, "zfs snap '%s' was created for '%s'!" % (jzsnap, self.jail['name']), 1)
          return jzsnap
      except:
          log(2, "jadm was not able to create '%s' zfs snap for '%s'!" % (jzsnap, self.jail['name']), 1)
          return False

   def remove(self, jail, num):
# remove jail zfs snapshot
      if self.find_jail(jail) == False or self.list(jail, 0) == False:
          return False
      check = []
      for i in self.list(jail, 0):
          check.append(str(i[0]))

      if num not in check:
          self.list(jail)
          log(2, "'%s' is not a '%s' jail snapshot number!" % (num, self.jail['name']))
          return False
      rmsnap = self.list(jail, 0)[int(num)][1]
      try:
          os.system(" zfs destroy -rf "+ rmsnap)
          log(1, "'%s' zfs snap '%s' was removed!" % (self.jail['name'], rmsnap), 1)
          return True
      except:
          log(2, "jadm was not able to remove '%s' zfs snap for '%s'!" % (rmsnap, self.jail['name']), 1)
          return False

   def restore(self, jail, num):
# restore jail snapshot
      if self.find_jail(jail) == False or self.list(jail, 0) == False:
          return False

      if self.jail['active'] != 0:
          log(2, "zfs snapshot cannot be restored when '%s' is running or dying!" % (self.jail['name']))
          return False

      check = []
      for i in self.list(jail, 0):
          check.append(str(i[0]))

      if num not in check:
          self.list(jail)
          log(2, "'%s' is not a '%s' jail snapshot number!" % (num, self.jail['name']))
          return False
      resnap = self.list(jail, 0)[int(num)][1]

      log(1, "'%s' will be restored from '%s' zfs snapshot!" % (self.jail['name'], resnap))
      log(1, "all zfs snapshots newer than '%s' will be removed!" % resnap)

      if autoc != 'y':
          yn = raw_input('confirm (y):> ')
      else:
          yn = 'y'

      if 'y' not in yn:
          return False

      try:
         os.system("zfs rollback -r %s" % resnap)
         log(0, "'%s' was restored from '%s' zfs snapshot!" % (self.jail['name'], resnap), 1)
         return True
      except:
          log(2, "jadm was not able to restore '%s' zfs snap for '%s'!" % (resnap, self.jail['name']), 1)
          return False

########################################################################################
# MIGRATION BETWEEN HOSTS
########################################################################################

class net_server:

   def __init__(self, jname = None, host = None, port = None, jpath = None):
       self.host = host
       self.port = int(port)
       self.jname = jname
# check if we call client or server (0 for server)
       if jpath != 0:
          pz = jpath.split(':')
          self.jpath = pz[0]
          self.zfs = None
          if len(pz) == 2:
             self.zfs = pz[1]

   def find_jail(self, jnid):
# check if jail exist
      self.jail = check_jail_Setting('modify', jnid)
      if self.jail == False:
         return False
      else:
         return self.jail

   def server(self):
      '''
########################################################################################
#       recv aes encrypted data
#
      '''

      CONNECTION_LIST = []    # list of socket clients
      self.RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2
      isbin = 0

      try:
         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         s.bind((self.host, self.port))
         s.listen(10)
         log(0, "jadm start server '%s:%s'" % (self.host, self.port), 1)
      except:
         log(2, "jadm can't bind '%s:%s' address" % (self.host, self.port), 1)
         return False

# Add server socket to the list of readable connections
      CONNECTION_LIST.append(s)

      while 1:
# Get the list sockets which are ready to be read through select
         read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])
         for sock in read_sockets:
#New connection
            if sock == s:
                # Handle the case in which there is a new connection recieved through server_socket
                sockfd, addr = s.accept()
                CONNECTION_LIST.append(sockfd)
                log(0, "jadm server is connected by: '%s:%s'" % (addr[0], addr[1]), 1)
                self.ip, self.cport = addr[0], addr[1]
#Some incoming message from a client
            else:
# Data recieved from client, process it
                try:

                    if isbin == 1:
                          while True:
                             recvdata = sock.recv(self.RECV_BUFFER)
                             if not recvdata:
                                 isbin = 0
                                 rcvfile = '%s/%s' % (self.rcfdir, self.migfile)
                                 buf.close()
                                 log(0, "'%s/%s' was received from '%s:%s' -> md5( %s )" % (self.rcfdir, self.migfile, addr[0], addr[1], self.fileMD5))
                                 break
                             buf.write(recvdata)
# check md5 sum
                          rcvfileMD5 = hashlib.md5(open(rcvfile).read()).hexdigest()
                          if self.fileMD5 != rcvfileMD5:
                              log(2, "'%s/%s' received md5 ( s% ) sum is not same as expected ( %s ) !" % (self.migpath, self.migfile, self.fileMD5, rcvfileMD5))
                          else:
                              self.RestoreJail()
                              sock.close()
                              CONNECTION_LIST.remove(sock)
                              continue

#In Windows, sometimes when a TCP program closes abruptly,
# a "Connection reset by peer" exception will be thrown
                    recvdata = sock.recv(self.RECV_BUFFER)

                    # echo back the client message
                    if recvdata:
# shutdown server if receive 'closecon'
                        if recvdata == 'closecon':
                           log(1, "jadm server was stopped by '%s:%s'!" % (addr[0], addr[1]), 1)
                           s.close()
                           return False

                        if recvdata == 'isbin':
                            buf = open('%s/%s' % (self.rcfdir, self.migfile), 'w')
                            isbin = 1

                        if recvdata == 'no_isbin':
                            isbin = 0

                        data = recvdata.split(' ')
# check and create path for new migrated jail
                        if data[0] == 'jail':
# migrated jail name
                              self.migname = data[1]
                              if check_jail_Setting('name', data[1]) == False:
                                  log (2, "'%s' cannot be received from '%s:%s'"  % (data[1], addr[0], addr[1]), 1)
                                  sock.send('jexist')
                                  sock.close()
                                  CONNECTION_LIST.remove(sock)
                                  continue
                              else:
                                  log(0, "jail with name '%s' will be received from '%s:%s'" % (data[1], addr[0], addr[1]), 1)
# migrated jail path
                              self.migpath = '%s/%s' % (data[2], data[1])
                              if check_jail_Setting('path', self.migpath) == False:
                                log (2, "'%s' cannot be received from '%s:%s'"  % (data[1], addr[0], addr[1]), 1)
                                sock.send('direxist')
                                sock.close()
                                CONNECTION_LIST.remove(sock)
                                continue
                              else:
                                 subprocess.call(['mkdir', '-p', '%s' % (self.migpath)])
                                 log(0, "'%s' directory was create by '%s:%s'" % (self.migpath, addr[0], addr[1]), 1)

                              self.rcfdir = data[2]
                              self.migfile = data[3]
                              self.fileMD5 = data[4]
# migrate zfs
                              self.jtype = data[5]
                              self.jzfs = data[6]
                              if self.jtype == 'zfs':
                                 if zfs('%s/%s' % (self.jzfs, self.migname))[2] == 1:
                                    log(2, "'%s/%s' already exist and cannot be used!" % (self.jzfs, self.migname), 1)
                                    sock.send('zfs')
                                    sock.close()
                                    CONNECTION_LIST.remove(sock)
                                    continue
                                 else:
                                    try:
                                       subprocess.check_call('zfs create %s/%s' % (self.jzfs, self.migname), shell=True)
                                       subprocess.check_call('zfs set mountpoint=%s  %s/%s' % (self.migpath, self.jzfs, self.migname), shell=True)
                                       log(0, "zfs '%s/%s' was created by '%s:%s'!" % (self.jzfs, self.migname, addr[0], addr[1]), 1)
                                    except subprocess.CalledProcessError:
                                       log(2, "zfs '%s/%s' cannot be created!" % (self.jzfs, self.migname), 1)
                                       os.system('rm -rf %s' % (self.migpath))
                                       sock.send('zfs')
                                       sock.close()
                                       CONNECTION_LIST.remove(sock)
                                       continue

                        sock.send(recvdata)
# client disconnected, so remove from socket list
                except:
                    log(2, "'%s:%s' jadm client is offline!" % (addr[0], addr[1]), 1)
                    sock.close()
                    CONNECTION_LIST.remove(sock)
                    continue

# close socket server
      s.close()

   def migrate_client(self):
      '''
########################################################################################
#   migrate jail from localhost to remote host
#
      '''
      jails = load_jail_config(jailconf)[0]
# find jail name
      jail = self.find_jail(self.jname)
      if jail == False:
          return False

      self.remote_path = self.jpath

      if '/' not in self.remote_path:
          log(2, "'%s' is a not valid os path for '%s'!" % (self.remote_path, self.host), 1)
          return False

# find jail
      for i in jails:
       if i['name'] == jail:
           jail = i
           break

# if this jail i running
      if jail['active'] == 1:
         log(2, "jail '%s' is running and cannot be migrated!" % jail['name'])
         return False

# create tar file without encription
      log(0, "jail '%s' will be migrated to '%s:%s/%s'!" % (jail['name'], self.host, self.remote_path, jail['name']), 1)

### NETWORK CLIENT ###
      try:
         self.cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         self.cs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         self.cs.connect((self.host, self.port))
      except socket.error:
         log(2, "no connection to %s:%s" % (self.host, self.port))
         return False

# Create jail archive/zfs
      try:
         if jail['zfs'] == 0:
             mfile = '%s/../migration_backup_%s.tar.gz' % (jail['path'], jail['name'])
             os.system('tar czPf %s -C %s .' % (mfile, jail['path']))
             file_an = 'migration_backup_%s.tar.gz' % jail['name']
             jtype = 'tar'
         else:
             mfile = '%s/../migration_backup_%s.zfs' % (jail['path'], jail['name'])
             jsnap = jail_zfs_snap()
             zfssnap = jsnap.create(jail['name'])
             os.system('zfs send %s > %s' % (zfssnap, mfile))
             os.system('zfs destroy -rf %s' % (zfssnap))
             file_an = 'migration_backup_%s.zfs' % jail['name']
             jtype = 'zfs'
# check md5 sum
         mfileMD5 = hashlib.md5(open(mfile).read()).hexdigest()
         log(0, "%s was created! -> md5( %s )" % (mfile, mfileMD5))
      except:
             log(2, "jadm cannot create '%s' file!" % mfile, 1)
             return False

# MIGRATION

# step 1 - send jail name and path
      sendData = 'jail %s %s %s %s %s %s' %  (jail['name'], self.remote_path, file_an, mfileMD5, jtype, self.zfs)
      self.cs.send(sendData)
      rcvData = self.cs.recv(1024)

      if rcvData != sendData:
          if rcvData == 'jexist':
              log (2, "'%s' cannot be send to '%s:%s' -> already exist!"  % (jail['name'], self.host, self.port), 1)
          if rcvData == 'direxist':
              log (2, "'%s:%s:%s' cannot be created -> already exist!!"  % (self.host, self.port, self.remote_path), 1)
          if rcvData == 'zfs':
              log(2, "zfs '%s/%s' cannot be created by '%s:%s'!" % (self.zfs, jail['name'], self.host, self.port), 1)
          os.system('rm -rf %s' % mfile)
          log (1, "'%s' was destroyed!" % mfile)
          self.cs.shutdown(socket.SHUT_RDWR)
          self.cs.close()
          return False

# step 2 - announce BIN data
      sendData = 'isbin'
      self.cs.send(sendData)
      rcvData = self.cs.recv(1024)

# step 3 - send zfs/tar file
      send_file = open(mfile, "rb")
      while True:
         chunk = send_file.read(65536)
         if not chunk:
            break  # EOF
         self.cs.sendall(chunk)
# close all connections
      self.cs.shutdown(socket.SHUT_RDWR)
      self.cs.close()
# display end msg
      os.system('rm -rf %s' % mfile)
      log (1, "'%s' was destroyed!" % mfile)
      log(0, "'%s' migration was complete please check '%s' for status!" % (jail['name'], self.host), 1)
      return True

   def RestoreJail(self):
      '''
########################################################################################
# restore migrated jail
#
      '''
# try to restore jail form file
      log(0, "start restoring '%s' to '%s'!" % (self.migfile, self.migpath), 1)
      log(0, "jail type '%s'" % self.jtype, 1)
      try:
         if self.jtype == 'tar':
            subprocess.call(['tar', '-xzf', '%s/%s' % (self.rcfdir, self.migfile),  '-C', self.migpath])

         if self.jtype == 'zfs':
            rcmd = 'zfs receive -F %s/%s < %s/%s' % (self.jzfs, self.migname, self.rcfdir, self.migfile)
            os.system(rcmd)

         log(0, "restoring '%s' to '%s' was completed!" % (self.migfile, self.migpath), 1)
         log(1, "please remove '%s/%s' manually!" % (self.rcfdir, self.migfile))
      except:
         log(2, "jadm cannot restore '%s' to '%s'!" % (self.migfile, self.migpath), 1)
         return False

# try to import jail after restring
      try:
          import_jail(self.migpath)
          log(0, "'%s' was migrated successful from '%s:%s'!" % (self.migname, self.ip, self.cport), 1)
          return True
      except:
         log(2, "jadm cannot import '%s' from '%s'!" % (self.migname, self.migpath), 1)
         return False

def find_jail(val):
   '''
########################################################################################
#  check if jail exist
#
   '''
   jails = load_jail_config(jailconf)[0]

# check ifi jail exist
   for j in jails:
      if j['name'] == val:
         return j
      if j['jid'] == val:
         return j
      if val == 'BASE':
         return False
   log(2, "'%s' is a not existing jail!" % val)
   return False

class archive:
    '''
########################################################################################
# tar archive/restore
#
    '''
    def __init__(self, jail, sd):
        self.jail = jail
        self.sd = sd
        self.dt = str(datetime.now().strftime('%Y-%m-%d_%s'))
        self.archname = "%s_%s.tar.gz" % (self.jail['name'], self.dt)
        self.jpath = self.jail['path']
        if self.jail['SKEL'] == 1:
            self.archfolder = './rw'
        else:
            self.archfolder = '.'

    def create(self):
        if self.jail['active'] == 1:
           log(1, "jail '%s' is running!" % self.jail['name'])
        if os.path.isdir(self.sd) == False:
            log(2, "'%s' not existing folder!" % self.sd)
            return False

        log(0, "archive path: %s" % self.jpath)
        log(0, "make '%s' > '%s/%s' archive in progress!" % (self.jpath, self.sd, self.archname))
        try:
           subprocess.check_call(['tar', '-czf', '%s/%s' %(self.sd, self.archname), '-C', self.jpath, self.archfolder])
        except:
           log(2, "'%s/%s' archive was not created!" % (self.sd, self.archname))
           return False
        log(0, "'%s/%s' archive was created!" % (self.sd, self.archname))
        return True

    def restore(self):
        if self.jail['active'] == 1:
           log(2, "jail '%s' is running!" % self.jail['name'])
           return False
        if os.path.isfile(self.sd) == False and '.tar.gz' not in self.sd:
           log(2, "'%s' not existing tar file!" % self.sd)
           return False
        try:
           subprocess.check_call(['tar', '-zxf', self.sd, '-C', self.jpath])
        except:
           log(2, "'%s' archive was not restored!" % (self.sd))
           return False
        log(0, "'%s' archive was restored!" % (self.sd))
        return True

########################################################################################
# MAIN MENU
########################################################################################

class lcmd(cmd.Cmd):
   """
########################################################################################
#  interactive main menu with 'cmd' function
#
   """
   jails_config = load_jail_config(jailconf)[0]
   prompt = 'jadm:> '

   jails = []
   for i in jails_config:
       jails.append(i['name'])

   local_global = ['remove',  'add']
   snap = ['create',  'remove',  'restore']
   listcmd = ['list', 'hostname',  'ip',  'gw',
              'active',  'inactive', 'dying',
              'vnet', 'novnet', 'zfs', 'nozfs',
              'skel', 'noskel', 'quota', 'used', 'empty']
   mserver = ['server',  'client']
   archive = ['create',  'restore']

   def emptyline(self):
         pass

   def default(self, line):
      log(2, "'%s' not working, please use 'help' or '-h only for cli'" % line)
      return cmd.Cmd.default(self, line)

   def do_list(self, arg):
      """
List of jails
-------------
-- FLAGS
   S - skeleton model jail
   V - vnet jail
   Z - zfs jail
   _DYING - dying jail

-- USAGE: list
   (search options)
   - only actvie jails (usage: list active)
   - only inactive jails (usage: list inactive)
   - only dying jails (usage: list dying)
   - no fully configured jails (usage: list empty)
   - by name (usage: list 'name/jid' )
   - by hostname (usage: list hostname 'jail hostname' )
   - by path (usage: lisy path '/jail/home/path' )
   - by ipaddress (usage: list ip 'ipaddress' )
   - by gateway (usage: list gw 'gateway ip' )
   - only vnet jails (usage: list vnet )
   - only none vnet jails (usage: list novnet )
   - only skel jails (usage: list skel )
   - only none skel jails (usage: list noskel )
   - only none zfs jails (usage list nozfs)

-- ZFS JAILS
   - only zfs jails (usage: list zfs)
   - by zfs quota (usage: list quota 'xxx('K', 'M', 'G', 'T', 'P', 'E')')
   show jails with quota = or > form x number
   - by zfs used space (usage: list used 'xxx('K', 'M', 'G', 'T', 'P', 'E')')
   show jails with used space = or > form x number
      """
      arg = str(arg).split(' ')

      if arg[0] != '':
         arg.insert(0,'list')
         arg = [i for i in arg if i != '']

      jail_table(arg)

   def complete_list(self, text, line, begidx, endidx):
      if not text:
         completions = self.listcmd[:]
      else:
         completions = [ f for f in self.listcmd if f.startswith(text)]
         return completions

   def do_jls(self,  arg):
      """
List of jails in short format
-----------------------------
-- FLAGS
   S - skeleton model jail
   V - vnet jail
   Z - zfs jail
   _DYING - dying jail

-- USAGE: jls
   (search options)
   - only actvie jails (usage: jls active)
   - only inactive jails (usage: jls inactive)
   - only dying jails (usage: jls dying)
   - no fully configured jails (usage: jls empty)
   - by name (usage: jls 'name/jid' )
   - by hostname (usage: jls hostname 'jail hostname' )
   - by path (usage: lisy path '/jail/home/path' )
   - by ipaddress (usage: jls ip 'ipaddress' )
   - by gateway (usage: jls gw 'gateway ip' )
   - only vnet jails (usage: jls vnet )
   - only none vnet jails (usage: jls novnet )
   - only skel jails (usage: jls skel )
   - only none skel jails (usage: jls noskel )
   - only none zfs jails (usage jls nozfs)

-- ZFS JAILS
   - only zfs jails (usage: jls zfs)
   - by zfs quota (usage: jls quota 'xxx('K', 'M', 'G', 'T', 'P', 'E')')
   show jails with quota = or > form x number
   - by zfs used space (usage: jls used 'xxx('K', 'M', 'G', 'T', 'P', 'E')')
   show jails with used space = or > form x number
      """

      arg = str(arg).split(' ')
      arg.insert(0,'jls')
      arg = [i for i in arg if i != '']

      jail_table(arg)

   def complete_jls(self, text, line, begidx, endidx):
      if not text:
         completions = self.listcmd[:]
      else:
         completions = [ f for f in self.listcmd if f.startswith(text)]
         return completions

   def do_gateways(self,  arg):
      """
Show system 'bridgeX' ip addresses and OS (system) default gateway
used for jails gateways.
------------------------------------------------------------------

-- USAGE: gateways
      """

      gateways(gwtable = 1)

   def do_globals(self,  arg):
      """
Globals settings applayed to all jails
--------------------------------------

-- USAGE: globals
   - list of globals settings (usage: globals)
   - add global setting (usage: globals add 'setting=val')
   - remove global setting (usage: globals remove 'setting number' )
      """

      arg = str(arg).split(' ')
      arg = [i for i in arg if i != '']

      if len(arg) > 1:
         globals(arg[0], arg[1])
      else:
         globals()

   def complete_globals(self, text, line, begidx, endidx):
      if not text:
         completions = self.local_global[:]
      else:
         completions = [ f for f in self.local_global if f.startswith(text)]
         return completions

   def do_locals(self,  arg):
      """
Jails Local settings
---------------------

-- USAGE: locals
   - list of jail locals settings (usage: locals 'name/jid')
   - add jail local setting (usage: locals 'name/jid' add 'setting=val')
   - remove jail local setting (usage: locals 'name/jid' remove 'setting number' )
      """

      arg = str(arg).split(' ')
      arg = [i for i in arg if i != '']

      if len(arg) == 3:
         locals(arg[0], arg[1], arg[2])
      elif len(arg) == 1:
         locals(arg[0])
      else:
         log(2, "Please enter 'help locals' for valid commands!")

   def complete_locals(self, text, line, begidx, endidx):
      if not text:
         completions = self.local_global[:]
      else:
         completions = [ f for f in self.local_global if f.startswith(text)]
         return completions

   def do_create(self, arg):
      """
Create new jail
---------------

-- USAGE: create
   - interactive create new jail (usage: create) without arguments

-- BASE JAIL
   - create new BASE jail (usage: create BASE 'bsd/src' '/top/BASE/folder')
      (bsd) install from base.txz
      (src) install from freebsd src /usr/src
   - install BASE ports tree (usage: create BASE ports install)
   - update BASE ports tree (usage: create BASE ports update)

-- IMPORT existing 'jadm' jail, /etc/jails.conf should exist under jail rootfolder
   - (usage: create /existing/jail/rootpath)

-- CLI USAGE
   - syntax
      option:value:value2

   - example
      jadm -y -c name:cli_jail hostname:cli_jail.local.lan id:12 ip:192.168.1.20 vnet:192.168.1.1 zfs:jails/cli_jail:3G path:/jails install:bsd

   -y (auto confirm)
   -c (create)

   - minimal
      name: jail name
      hostname: jail hostname
      id: jail id (jid)
      ip: jail ip address
      path: jails rootpath (jail will be created in folder under this one) - main jails folder
      install: install methods
         (bsd) install from base.txz
         (src) install from freebsd src /usr/src
         (skel) install skeleton (BASE) jail model
         (template) - install:template:jail(name/id) - use existing jail as template

   - optional
      (by default jadm not create zfs/vnet jails)
      vnet: create vnet jail - gateway ip address
      zfs: create zfs jail zfs:zroot/jail_zfs:quota 'xxx('K', 'M', 'G', 'T', 'P', 'E')'
      - can use and zpool only - zfs:zroot:quota
      """

      arg = str(arg).split(' ')
      arg = [i for i in arg if i != '']

      if len(arg) >= 1:
         if arg[0] == 'BASE':
            BASE(arg[1], arg[2])
         elif arg[0] == 'srcbuild':
            srcbuild()
         elif arg[0][0] == '/':
            import_jail(arg[0])
         elif ':' in arg[0]:
            remote_nfs_jail(arg[0], arg[1])
         else:
            pass
      else:
         jail_input()

   def do_modify(self, arg):
       """
Modify existing jail
--------------------

-- USAGE: create
   - interactive modify existing jail (usage: modify 'name/jid') without arguments

-- CLI USAGE
   - syntax
      option:original_value:new_value

   - examples
      jadm -m name:cli_jail3:jail3 id:34 ip:3.3.3.3 path:/jails/modjails
      jadm -m id:34 zfs:quota:4G
      jadm -m name:cli_jail3:new_jail3_name
      jadm -m id:34:23

   -y (auto confirm)
   -m (modify)

   - minimal
      name: new jail name
      id: new jail id (jid)

   - optional
      hostname: jail new hostname
      ip: jail ip address
      vnet: new vnet jail gateway
      path: new jail rootpath
      zfs: new jail rootzfs
      zfs:quota: new zfs quota 'xxx('K', 'M', 'G', 'T', 'P', 'E')'
       """
       jail_input(arg)

   def complete_modify(self, text, line, begidx, endidx):
      if not text:
         completions = self.jails[:]
      else:
         completions = [ f for f in self.jails if f.startswith(text)]
         return completions

   def do_destroy(self, arg):
        """
Destroy jail and delete jail data/zfs
-------------------------------------

-- USAGE: destroy
   - destroy jail (destroy 'name/jid')
   - destroy BASE jail and clear '/usr/freebsd-dist/' folder
     (destroy BASE clear)

        """
        arg = str(arg).split(' ')
        arg = [i for i in arg if i != '']
        try:
           if arg[0] == 'BASE' or arg[0] == '0':
              if arg[0] == '0':
                 arg[0] = 'BASE'
              if len(arg) > 1:
                 if arg[1] == 'clear':
                    jail_destroy(arg[0], 'destroy', 'clear')
           jail_destroy(arg[0], 'destroy')
        except:
           log(2, "Please enter 'help destroy' for valid commands!")

   def do_remove(self, arg):
        """

Remove jail but keep jail data/zfs
----------------------------------

-- USAGE: remove
   (remove 'name/jid')
        """
        jail_destroy(arg, 'remove')

   def do_about(self, arg):
       """
About JADM and license info
---------------------------

-- USAGE: about
       """
       print about()

   def do_exit(self, arg):
        """
Exit from JADM
--------------

-- USAGE: exit
        """
        print "Good bye!"
        sys.exit(0)

   def do_quit(self, arg):
        """
Exit from JADM
--------------

-- USAGE: quit
        """
        print "Good bye!"
        sys.exit(0)

   def do_start(self, arg):
        """
Start jail
----------

-- USAGE: start
   - start jail: (usage: start 'name/jid')
   - start all jails: (usage: start all)
        """
        startstop_jail(arg, 'start')

   def do_stop(self, arg):
        """
Stop jail
----------

-- USAGE: stop
   - stop jail: (usage: stop 'name/jid')
   - stop all jails: (usage: stop all)
        """
        startstop_jail(arg, 'stop')

   def do_reboot(self, arg):
        """
Reboot jail
-----------

-- USAGE: reboot
   - reboot jail: (usage: reboot 'name/jid')
   - reboot all jails: (usage: reboot all)
        """
        startstop_jail(arg, 'reboot')

   def do_shell(self, arg):
        """
Enter in Jail (jexec root shell)
--------------------------------

-- USAGE: shell
   (shell 'name/jid')
        """
        jail_shell(arg)

   def do_verbose(self, arg):
        """
Verbose output when start/stop jail
-----------------------------------

-- USAGE: verbose
   (by default is off)

-- CLI USAGE
   -v (verbose output)
        """
        global verbose
        if verbose == 0:
           verbose = 1
           log(0, "start/stop verbose mode enabled")
        else:
           verbose = 0
           log(0, "start/stop verbose mode disabled")

   def do_snap(self, arg):
        """
Jail ZFS snapshots
------------------

-- USAGE: snap
   - list jail zfs snapshot (usage: snap 'name/jid')
   - create jail zfs snapshot (usage: snap 'name/jid' create)
   - remove jail zfs snapshot (usage: snap 'name/jid' remove 'sanp number')
   - restore jail zfs snaphsot (usage: snap 'name/jid'restore 'snap number')
        """
        arg = str(arg).split(' ')
        arg = [i for i in arg if i != '']
        jlist = jail_zfs_snap()

        if len(arg) == 1:
           jlist.list(arg[0])

        elif len(arg) == 2 and arg[1] == 'create':
           jlist.create(arg[0])

        elif len(arg) == 3 and  arg[1] == 'remove':
           jlist.remove(arg[0], arg[2])

        elif len(arg) == 3 and  arg[1] == 'restore':
           jlist.restore(arg[0], arg[2])
        else:
           log(2, "Please enter 'help snap' for valid commands!")

   def complete_snap(self, text, line, begidx, endidx):
        if not text:
            completions = self.snap[:]
        else:
            completions = [ f
                            for f in self.snap
                            if f.startswith(text)
                            ]
        return completions

   def do_server(self, arg):
        """
Jadm NOT encrypted network server - accept migrated jails (NO CLI)
------------------------------------------------------------------

-- USAGE: server
   - start net server (usage: server)
   (by default use ip:0.0.0.0 and port:4555)
   - set ip address (usage: sever ip:x.x.x.x)
   - set port (usage server port:xxxx)
   - start with set ip/port (usage: server ip:x.x.x.x port:xxxx)

        """
        arg = str(arg).split(' ')
        arg = [i for i in arg if i != '']

        global LHOST
        global LPORT
        nhost = LHOST
        nport = LPORT

        if len(arg):
           for i in arg:
              hp = i.split(':')
              if hp[0] == 'port':
                 try:
                     int(hp[1])
                     nport = hp[1]
                 except:
                     log(2, "'%s' is not a valid network port number!" % hp[1])
                     return False
              if hp[0] == 'host':
                 nhost = hp[1]

        jserver = net_server(0, nhost, nport, 0)
        t = threading.Thread(target=jserver.server)
        t.daemon = True
        t.start()

   def do_migrate(self, arg):
        """
Migrate jail(s) to remote server (NO encryption!)
-------------------------------------------------

-- USAGE: migrate
   - syntax
     migrate re.mo.te.ip:port 'name/jid'=/remote/jail/rootpath(:zfs/path)
     (separate jai; name/jid and remote jaisl path:zfs with '=')

   - examples
   (migrate one jail)
    migrate 192.168.1.32:4555 migjail=/jails/migrated

   (migrate zfs jail)
    migrate 192.168.1.32:4555 migjail=/jails/migrated:zroot/jails

   (migrate multiple jails separated with ',' - use default port)
    migrate 192.168.1.32 migjail=/jails/migrated, 34=/jails/migrated , otherjail=/jails/migrated:zroot/jails

   - minimal
      - destination server ip address
      - jail name/id
      - remote jail rootpath

   - optional
      - :port - destination server port
      - :zfs/path - remote jail zfs

        """
        arg = str(arg).split(' ')
        arg = [i for i in arg if i != '']

        global LHOST
        global LPORT
        nhost = LHOST
        nport = LPORT

# use custom port if is needed
        try:
           server = str(arg[0]).split(':')
           rip = server[0]
           if len(server) == 2:
              if server[1] != nport:
                rport = server[1]
           else:
              rport = nport
        except:
              log(2, "Please enter 'help migrate' for valid commands!")
              return False

# list for multiple jails and migrate
        mjails = ' '.join(arg[1:]).split(',')
        for i in mjails:
            mj = i.split('=')
            jclient = net_server(mj[0].strip(' '), rip, rport, mj[-1].strip(' '))
            jclient.migrate_client()

   def do_cli(self, arg):
      """
Jadm Operating System Command Line Interface
Can use any jadm command directly from OS cli (except 'server')
---------------------------------------------------------------

-- USAGE: cli
    jadm (-y, -c, -m, -v, -a, -i, -h) command ..., -update

-- KEYS
   -y (auto confirm) will not prompt for any conformation!
   -c (create new jail ) cannot be used wiht '-m'
   -m (modify existing jail) cannot be used with '-c'
   -v (verbose when start/stop jail)
   -i (jadm info)
   -a (jadm about)
   -h (help) list of jadm commands
   -update (try to update /etc/jail.conf with jadm ver.2x format)
   -- jadm cannot guaranty 100% success, backup file will be created.
      """
      lcmd().onecmd('help cli')

   def do_archive(self, arg):
     """
-- USAGE: archive
   - create new tar archive (usage: archive 'name/jid' create '/tar/archive/destination/folder')
   - restore tar archive (usage: archive 'name/jid' restore '/destination/folder/file name.tar.gz')
     """
     arg = str(arg).split(' ')
     arg = [i for i in arg if i != '']

     if len(arg) == 3 and arg[1] == 'create':
        jail = find_jail(arg[0])
        if jail:
           arch = archive(jail, arg[2])
           arch.create()
     elif len(arg) == 3 and  arg[1] == 'restore':
        jail = find_jail(arg[0])
        if jail:
           arch = archive(jail, arg[2])
           arch.restore()
     else:
        log(2, "Please enter 'help archive' for valid commands!")

   def complete_archive(self, text, line, begidx, endidx):
        if not text:
            completions = self.archive[:]
        else:
            completions = [ f
                            for f in self.archive
                            if f.startswith(text)
                            ]
        return completions

########################################################################################
# Start Jadm script
########################################################################################

if len(sys.argv) > 1:
# use cli interface
   cli()
else:
# main menu shell
   welcome()
   lcmd().cmdloop()

########################################################################################
# End Jadm script
########################################################################################
