Skip to content

নেটমিকো/ওয়াইএমএল দিয়ে কনফিগারেশন পুশ

আপনার এক বন্ধু আছে উজ্জল। সে একটা বড় টেলিকম কোম্পানিতে নেটওয়ার্ক স্পেশালিস্ট। একদিন অফিসে যাবার পর তার বস বলল, "উজ্জল, আমাদের কাস্টমারের ৫০০টা রাউটারে নতুন কনফিগারেশন দিতে হবে। কাল সকালের মধ্যে চাই।"

উজ্জল প্রথমে একটু ঘাবড়ে গেল। মনে মনে হিসাব করল - প্রতি রাউটারে ১০ মিনিট, ৫০০টা রাউটার, মোট লাগবে ৮৩ ঘণ্টা! কিন্তু উজ্জল হাল ছাড়ল না। আসলে সে জানে এই কাজটা পাইথন দিয়ে অটোমেট করে ১৫ মিনিটেই শেষ করা যায়।

চলুন দেখি কীভাবে উজ্জল এই চ্যালেঞ্জ ফাইনালি ক্লোজ করল।

স্টেপ ১: প্রাথমিক পরিকল্পনা

উজ্জল প্রথমে তার কাজের প্ল্যানে কিছু জিনিস যোগ করলো:

  1. নেটওয়ার্কিং প্ল্যান:

    • প্রতিটা রাউটারের জন্য দুটা করে আইপি লাগবে:
      • ম্যানেজমেন্ট আইপি (192.168.0.0/23 রেঞ্জ থেকে)
      • লুপব্যাক আইপি (10.0.x.y রেঞ্জ থেকে)
    • ম্যানেজমেন্ট আইপি দিয়ে রাউটারে ঢুকবে
    • লুপব্যাক আইপি হবে রাউটারের ফিক্সড আইডেন্টিটি
  2. টুল সিলেকশন:

    • YAML: ডাটা স্টোর করার জন্য
    • Jinja2: কনফিগারেশন টেমপ্লেট বানানোর জন্য
    • Netmiko: রাউটারে কানেক্ট করে কমান্ড পাঠানোর জন্য
    • Python ipaddress: আইপি এড্রেস ক্যালকুলেশনের জন্য
  3. প্রজেক্ট স্ট্রাকচার:

network_automation/
├── templates/            # টেমপ্লেট ফাইল
│   └── loopback.j2      # রাউটার কমান্ড টেমপ্লেট
├── scripts/             # পাইথন স্ক্রিপ্ট
│   └── deploy.py        # মূল প্রোগ্রাম
└── configs/             # ভবিষ্যতে ব্যবহারের জন্য
    └── (ফাইলগুলো পরে যোগ করা হবে, আমাদের ডাটাবেজ তৈরি হলে)

স্টেপ ২: কাজের প্রিপারেশন

প্রথমে আমাদের দরকারি টুল ইনস্টল করতে হবে। এজন্য পাইথন ভার্চুয়াল এনভায়রনমেন্ট তৈরি করে নিই:

# প্রথমে একটা আলাদা পরিবেশ তৈরি করি যাতে অন্য প্রোজেক্টের টুল মিশে না যায়
python -m venv network_env

# নতুন পরিবেশে চলে আসি
source network_env/bin/activate

# দরকারি টুল ইনস্টল করি
pip install netmiko pyyaml jinja2

এরপর আমাদের টেমপ্লেট ফাইল তৈরি করি। এই টেমপ্লেট অনুযায়ী প্রতিটা রাউটারে কমান্ড যাবে (এটা একটা বেসিক স্ট্রাকচার):

interface Loopback{{ interface.number }}
 description {{ interface.description }}
 ip address {{ interface.ip }} {{ interface.mask }}
 no shutdown

এই টেমপ্লেট কীভাবে কাজ করে:

  • {{ ... }} এর মধ্যে যা আছে সেগুলো ভ্যারিয়েবল
  • এই ভ্যারিয়েবলগুলোর জায়গায় আসল ভ্যালু বসবে
  • যেমন {{ interface.number }} এর জায়গায় বসবে 0
  • {{ interface.ip }} এর জায়গায় বসবে যেমন 10.0.0.1

স্টেপ ৩: মূল প্রোগ্রামের ধারণা

এবার আসি মূল প্রোগ্রামে। এটাকে আমরা চারটা অংশে ভাগ করে বুঝব:

  1. পাইথনের দরকারি লাইব্রেরি ইম্পোর্ট:
    import yaml                                    # ডাটা স্ট্রাকচার হ্যান্ডলিং
    from jinja2 import Environment, FileSystemLoader    # টেমপ্লেট ইঞ্জিন
    from netmiko import ConnectHandler            # দূরবর্তী রাউটার কানেকশন
    import ipaddress                              # আইপি গণনা
    

এখানে প্রতিটা লাইব্রেরির একটা নির্দিষ্ট কাজ:

  • yaml: এটা পাইথনের ডিকশনারি/লিস্টকে YAML ফরম্যাটে রূপান্তর করে
  • jinja2: এটা টেমপ্লেট ফাইল থেকে ডায়নামিক টেক্সট জেনারেট করে
  • netmiko: এটা SSH দিয়ে রাউটারে কানেক্ট করে কমান্ড পাঠায়
  • ipaddress: এটা আইপি এড্রেস নিয়ে কাজ করে

স্টেপ ৪: লুপব্যাক কনফিগারেশন জেনারেটর

প্রথমে আমরা দেখব কীভাবে প্রতিটা রাউটারের জন্য আলাদা লুপব্যাক ইন্টারফেস কনফিগারেশন তৈরি করা হয়:

def generate_interface_config(router_number):
    """
    প্রতিটা রাউটারের জন্য আলাদা লুপব্যাক আইপি জেনারেট করে

    কীভাবে কাজ করে:
    - রাউটার ১-৫০০ পর্যন্ত নাম্বার নেয়
    - প্রতিটার জন্য আলাদা আইপি দেয়:
      router_1   -> 10.0.0.1
      router_2   -> 10.0.0.2
      router_256 -> 10.0.1.0
      router_500 -> 10.0.1.244

    আইপি ক্যালকুলেশনের উদাহরণ:
    যদি router_number = 257 হয়:
    - third_octet = (257-1) // 256 = 1     # প্রতি 256 রাউটারে এটা 1 বাড়ে
    - fourth_octet = (257-1) % 256 + 1 = 1 # 1-256 পর্যন্ত চক্রাকারে ঘোরে

    তাই router_257 এর আইপি হবে: 10.0.1.1 
    """
    # আইপি এর তৃতীয় এবং চতুর্থ অক্টেট হিসাব করি
    third_octet = (router_number - 1) // 256   
    fourth_octet = (router_number - 1) % 256 + 1   

    # উদাহরণ: router_257 এর জন্য
    # third_octet = (257-1) // 256 = 1
    # fourth_octet = (257-1) % 256 + 1 = 1
    # তাই আইপি হবে 10.0.1.1

    return {
        'interfaces': {
            'loopback': {
                'number': 0,                    
                'description': f'Management Interface Router {router_number}',
                'ip': f'10.0.{third_octet}.{fourth_octet}',
                'mask': '255.255.255.255'      # একটা আইপির জন্য /32 মাস্ক
            }
        }
    }

এখানে আইপি তৈরির লজিক বুঝি:

  • আমরা 10.0.x.y প্যাটার্ন ব্যবহার করছি
  • প্রথম দুই অক্টেট ফিক্সড (10.0)
  • তৃতীয় অক্টেট (x) প্রতি 256 রাউটারে একটা করে বাড়ে
  • চতুর্থ অক্টেট (y) 1-256 পর্যন্ত ঘুরে ফিরে আসে

একটা ছোট উদাহরণ দিই:

  • router_1: 10.0.0.1
  • router_255: 10.0.0.255
  • router_256: 10.0.0.256 হবে না, হবে 10.0.1.0
  • router_257: 10.0.1.1

এভাবে ৫০০ রাউটারের জন্য ৫০০টা আলাদা আইপি তৈরি হবে। নেক্সট পার্টে আমরা দেখব কীভাবে রাউটার কনফিগারেশন জেনারেট করা হয়। আমি এবার পুরো কোডটা আরও ডিটেইলে এক্সপ্লেইন করে বলছি।

স্টেপ ৫: রাউটার কনফিগারেশন জেনারেটর

এবার আমরা দেখব কীভাবে ৫০০টা রাউটারের জন্য মূল কনফিগারেশন তৈরি করা হয়:

def generate_routers_config(start_ip, total_routers):
    """
    সব রাউটারের মূল কনফিগারেশন জেনারেট করে

    এখানে আমরা দুটো জিনিস লক্ষ্য রাখছি:
    ১. প্রতি রাউটারে পৌঁছানোর জন্য একটা আইপি লাগবে (ম্যানেজমেন্ট আইপি)
    ২. এই আইপিগুলো 192.168.0.0/23 নেটওয়ার্ক থেকে নেব

    একটা উদাহরণ:
    - router_1: 192.168.0.1 দিয়ে রাউটারে ঢুকব
    - router_2: 192.168.0.2 দিয়ে রাউটারে ঢুকব
    এভাবে চলতে থাকবে...
    """
    # /23 সাবনেট নেওয়ার কারণ:
    # - /23 মানে 512টা আইপি পাওয়া যায়
    # - আমাদের 500টা রাউটারের জন্য এটা যথেষ্ট
    # - নেটওয়ার্ক ও ব্রডকাস্ট আইপি বাদে 510টা ইউজেবল আইপি
    network = ipaddress.IPv4Network(f"{start_ip}/23", strict=False)
    ip_list = list(network.hosts())[:total_routers]

    # একটা খালি ডিকশনারি তৈরি করি
    routers = {
        'routers': []
    }

    # enumerate(ip_list, 1) ব্যবহার করছি কারণ:
    # - আমরা রাউটার নাম্বারিং 1 থেকে শুরু করতে চাই
    # - enumerate ডিফল্ট 0 থেকে শুরু করে
    for i, ip in enumerate(ip_list, 1):
        router = {
            'name': f'router_{i}',      # যেমন: router_1, router_2
            'ip': str(ip),              # যেমন: 192.168.0.1
            'username': 'admin',         # SSH ইউজারনেম
            'password': 'secure123',     # SSH পাসওয়ার্ড
            'type': 'cisco_ios'         # ডিভাইস টাইপ
        }
        routers['routers'].append(router)  # রাউটার অ্যাড হতে থাকবে

    return routers

এই কোডে কয়েকটা গুরুত্বপূর্ণ বিষয় লক্ষ্য করি:

  1. আইপি এড্রেসিং প্ল্যান:

  2. 192.168.0.0/23 নেটওয়ার্ক ব্যবহার করছি

  3. /23 মানে আমরা দুটো /24 নেটওয়ার্ক পাচ্ছি:
    • 192.168.0.0/24 (256টা আইপি)
    • 192.168.1.0/24 (256টা আইপি)
  4. মোট = 512টা আইপি, যা 500টা রাউটারের জন্য যথেষ্ট

  5. রাউটার কনফিগারেশন:

  6. প্রতিটা রাউটারের জন্য ৫টা ডাটা রাখছি

  7. নাম: যাতে সহজে চিনতে পারি
  8. আইপি: যা দিয়ে রাউটারে ঢুকব
  9. ইউজারনেম/পাসওয়ার্ড: SSH লগইনের জন্য
  10. টাইপ: রাউটারের মডেল টাইপ

চলুন আমরা এখন মেইন ফাংশনটা এক্সপ্লেইন করি। এটা আমাদের প্রোগ্রামের সবচেয়ে গুরুত্বপূর্ণ অংশ।

স্টেপ ৬: মেইন ফাংশন বিশ্লেষণ

মেইন ফাংশনটা তিনটা মূল ধাপে কাজ করে। আমি প্রতিটা ধাপ বিস্তারিতভাবে এক্সপ্লেইন করব:

def main():
    """
    মূল প্রোগ্রাম - এটা তিনটা ধাপে কাস্টমারের ৫০০টা রাউটার কনফিগার করে

    ধাপ ১: কনফিগারেশন জেনারেট
       - ৫০০টা রাউটারের ম্যানেজমেন্ট আইপি (192.168.0.0/23 থেকে)

    ধাপ ২: টেমপ্লেট প্রস্তুত
       - templates/loopback.j2 ফাইল থেকে টেমপ্লেট লোড

    ধাপ ৩: কনফিগারেশন পাঠানো
       - প্রতি রাউটারে দূরবর্তী সংযোগ স্থাপন
       - ইউনিক লুপব্যাক কনফিগ (10.0.x.y রেঞ্জ থেকে)
       - সংযোগ বন্ধ
    """
    # ধাপ ১: রাউটারের মূল তথ্য তৈরি
    routers = generate_routers_config('192.168.0.0', 500)

    # ধাপ ২: জিনজা টেমপ্লেট প্রস্তুত
    env = Environment(loader=FileSystemLoader('templates'))
    template = env.get_template('loopback.j2')

    # ধাপ ৩: প্রতিটা রাউটারে কাজ করি
    for i, router in enumerate(routers['routers'], 1):
        # এই রাউটারের জন্য বিশেষ লুপব্যাক আইপি নিই
        interfaces = generate_interface_config(i)

        # টেমপ্লেট থেকে কমান্ড তৈরি করি
        config = template.render(interface=interfaces['interfaces']['loopback'])

        print(f"\n{router['name']} ({router['ip']}) এ কাজ শুরু...")
        try:
            # দূরবর্তী রাউটারে সংযোগ স্থাপন
            connection = ConnectHandler(**router)

            # কনফিগারেশন কমান্ড পাঠাই
            output = connection.send_config_set(config.split('\n'))
            print(f"{router['name']} এ কাজ সফল!")

            # সংযোগ বন্ধ করি
            connection.disconnect()

        except Exception as e:
            # কোন সমস্যা হলে এরর মেসেজ দেখাই
            print(f"সমস্যা হয়েছে {router['name']} এ: {str(e)}")

এই কোডের প্রতিটা ধাপ বিস্তারিতভাবে বুঝি:

ধাপ ১ - কনফিগারেশন তৈরি:

  • প্রথমে generate_routers_config() ফাংশন কল করে ৫০০টা রাউটারের তথ্য নেই
  • এই তথ্যে থাকে প্রতি রাউটারের আইপি, নাম, লগইন ক্রেডেনশিয়াল
  • এটা একটা বড় ডিকশনারি হিসেবে থাকে

ধাপ ২ - টেমপ্লেট সেটআপ:

  • Jinja2 টেমপ্লেট ইঞ্জিন সেটআপ করি
  • 'templates' ফোল্ডার থেকে 'loopback.j2' ফাইল লোড করি
  • এই টেমপ্লেট ব্যবহার করে রাউটার কমান্ড তৈরি করব

ধাপ ৩ - মূল অটোমেশন প্রক্রিয়া বিস্তারিতভাবে বুঝি:

# প্রতিটা রাউটারের জন্য লুপ চালাচ্ছি
for i, router in enumerate(routers['routers'], 1):
    # এই রাউটারের জন্য বিশেষ লুপব্যাক আইপি নিই
    interfaces = generate_interface_config(i)

    # টেমপ্লেট থেকে কমান্ড তৈরি করি
    config = template.render(interface=interfaces['interfaces']['loopback'])

এই অংশে কী হচ্ছে তা একটা উদাহরণ দিয়ে বুঝি। ধরা যাক, আমরা router_1 এর জন্য কাজ করছি:

  1. প্রথমে generate_interface_config(1) কল করে এই রাউটারের লুপব্যাক কনফিগ পাই:

    {
        'interfaces': {
            'loopback': {
                'number': 0,
                'description': 'Management Interface Router 1',
                'ip': '10.0.0.1',
                'mask': '255.255.255.255'
            }
        }
    }
    

  2. এরপর টেমপ্লেট রেন্ডার করে সিসকো কমান্ড পাই:

    interface Loopback0
     description Management Interface Router 1
     ip address 10.0.0.1 255.255.255.255
     no shutdown
    

এরপর আসে সবচেয়ে গুরুত্বপূর্ণ অংশ - রাউটারে কানেক্ট করে কমান্ড পাঠানো:

try:
    # দূরবর্তী রাউটারে সংযোগ স্থাপন
    connection = ConnectHandler(**router)

    # কনফিগারেশন কমান্ড পাঠাই
    output = connection.send_config_set(config.split('\n'))
    print(f"{router['name']} এ কাজ সফল!")

    # সংযোগ বন্ধ করি
    connection.disconnect()

এখানে যেসব সমস্যা হতে পারে এবং কীভাবে হ্যান্ডেল করা হয়:

  1. কানেকশন সমস্যা:

  2. রাউটার অফলাইন থাকতে পারে

  3. নেটওয়ার্ক স্লো হতে পারে
  4. পাসওয়ার্ড ভুল হতে পারে

  5. কনফিগারেশন সমস্যা:

  6. কমান্ড সিনট্যাক্স ভুল হতে পারে

  7. আইপি কনফ্লিক্ট হতে পারে
  8. রাউটার মেমরি কম থাকতে পারে

এজন্য try-except ব্লক ব্যবহার করা হয়েছে। কোন সমস্যা হলে প্রোগ্রাম ক্র্যাশ না করে এরর মেসেজ দেখিয়ে পরের রাউটারে চলে যায়।

প্রোগ্রামের কাজের ধারা

                        [শুরু]
                  [টুল ইনস্টলেশন]
                [টেমপ্লেট প্রস্তুতি]
         [প্রথম রাউটার সিলেক্ট]←------┐
                           ↓           │
         [লুপব্যাক আইপি জেনারেট]       │
                           ↓           │
            [রাউটারে কানেক্ট]          │
                           ↓           │
         [কনফিগারেশন পাঠানো]          │
                           ↓           │
              [সফল?]--না-→[লগ করি]    │
                  ↓হ্যাঁ              │
         [পরের রাউটার আছে?]--হ্যাঁ-----┘
                  ↓না
                [শেষ]