<commit_before># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

""" Network Model

Provides network device listings and extended network information

"""

import logging
import textwrap

from urwid import (
    connect_signal,
    LineBox,
    ProgressBar,
    RadioButton,
    Text,
    )
from urwid import Padding as uPadding

from subiquitycore.ui.actionmenu import ActionMenu
from subiquitycore.ui.buttons import back_btn, cancel_btn, done_btn, menu_btn
from subiquitycore.ui.container import (
    Columns,
    ListBox,
    Pile,
    WidgetWrap,
    )
from subiquitycore.ui.form import Form, StringField, Toggleable
from subiquitycore.ui.stretchy import Stretchy
from subiquitycore.ui.table import ColSpec, TablePile, TableRow
from subiquitycore.ui.utils import button_pile, Color, make_action_menu_row, Padding
from subiquitycore.view import BaseView


log = logging.getLogger('subiquitycore.views.network')


class ApplyingConfigWidget(WidgetWrap):

    def __init__(self, step_count, cancel_func):
        self.cancel_func = cancel_func
        button = cancel_btn(_("Cancel"), on_press=self.do_cancel)
        self.bar = ProgressBar(normal='progress_incomplete',
                               complete='progress_complete',
                               current=0, done=step_count)
        box = LineBox(Pile([self.bar,
                            button_pile([button])]),
                      title=_("Applying network config"))
        super().__init__(box)

    def advance(self):
        self.bar.current += 1

    def do_cancel(self, sender):
        self.cancel_func()


def _build_wifi_info(dev):
    r = []
    if dev.actual_ssid is not None:
        if dev.configured_ssid is not None:
            if dev.actual_ssid != dev.configured_ssid:
                r.append(
                    Text(_("Associated to '%s', will "
                           "associate to '%s'" % (dev.actual_ssid,
                                                  dev.configured_ssid))))
            else:
                r.append(Text(_("Associated to '%s'" % dev.actual_ssid)))
        else:
            r.append(Text(_("No access point configured, but associated "
                            "to '%s'" % dev.actual_ssid)))
    else:
        if dev.configured_ssid is not None:
            r.append(Text(_("Will associate to '%s'" % dev.configured_ssid)))
        else:
            r.append(Text(_("No access point configured")))
    return r


def _format_address_list(label, addresses):
    if len(addresses) == 0:
        return []
    elif len(addresses) == 1:
        return [Text(label % ('',) + ' ' + str(addresses[0]))]
    else:
        ips = []
        for ip in addresses:
            ips.append(str(ip))
        return [Text(label % ('es',) + ' ' + ', '.join(ips))]


def _build_gateway_ip_info_for_version(dev, version):
    actual_ip_addresses = dev.actual_ip_addresses_for_version(version)
    configured_ip_addresses = dev.configured_ip_addresses_for_version(version)
    if dev.dhcp_for_version(version):
        if dev.actual_ip_addresses_for_version(version):
            return _format_address_list(_("Will use DHCP for IPv%s, currently "
                                          "has address%%s:" % version),
                                        actual_ip_addresses)
        return [Text(_("Will use DHCP for IPv%s" % version))]
    elif configured_ip_addresses:
        if sorted(actual_ip_addresses) == sorted(configured_ip_addresses):
            return _format_address_list(
                _("Using static address%%s for IPv%s:" % version),
                actual_ip_addresses)
        p = _format_address_list(
            _("Will use static address%%s for IPv%s:" % version),
            configured_ip_addresses)
        if actual_ip_addresses:
            p.extend(_format_address_list(_("Currently has address%s:"),
                                          actual_ip_addresses))
        return p
    elif actual_ip_addresses:
        return _format_address_list(_("Has no IPv%s configuration, currently "
                                      "has address%%s:" % version),
                                    actual_ip_addresses)
    else:
        return [Text(_("IPv%s is not configured" % version))]


from .network_configure_manual_interface import NetworkConfigForm

class EditNetworkStretchy(Stretchy):

    def __init__(self, parent, device, ip_version):
        self.parent = parent
        self.device = device
        self.ip_version = ip_version

        self.form = NetworkConfigForm(ip_version)

        connect_signal(self.form, 'submit', self.done)
        connect_signal(self.form, 'cancel', self.cancel)

        group = []
        b1 = RadioButton(group, _("Use a static IPv{ip_version} configuration").format(ip_version=ip_version))
        def b1cb(sender, state):
            if state:
                for field in self.form._fields:
                    field.enabled = True
            else:
                for field in self.form._fields:
                    field.enabled = False
                    field.showing_extra = False
                    field.validate()
            self.form.validated()
        connect_signal(b1, 'change', b1cb)
        b2 = RadioButton(group, _("Use DHCPv{ip_version} on this interface").format(ip_version=ip_version))
        b3 = RadioButton(group, _("Do not use"))
        t = Toggleable(Pile(self.form.as_rows()))
        widgets = [b1, Text(""), Padding.push_4(t), Text(""), b2, b3, Text(""), self.form.buttons]
        super().__init__(
            "Edit {device} IPv{ip_version} configuration".format(device=device.name, ip_version=ip_version),
            widgets,
            0, 0)

    def done(self, sender):
        # XXX this converting from and to and from strings thing is a
        # bit out of hand.
        gateway = self.form.gateway.value
        if gateway is not None:
            gateway = str(gateway)
        result = {
            'network': str(self.form.subnet.value),
            'address': str(self.form.address.value),
            'gateway': gateway,
            'nameservers': list(map(str, self.form.nameservers.value)),
            'searchdomains': self.form.searchdomains.value,
        }
        self.dev.remove_ip_networks_for_version(self.ip_version)
        self.dev.remove_nameservers()
        self.dev.add_network(self.ip_version, result)

        self.parent.refresh_model_inputs()
        self.parent.remove_overlay()

    def cancel(self, sender=None):
        self.model.default_gateway = None
        self.controller.network_configure_interface(self.dev.name)
        self.parent.remove_overlay()


class NetworkView(BaseView):
    title = _("Network connections")
    excerpt = _("Configure at least one interface this server can use to talk "
                "to other machines, and which preferably provides sufficient "<commit_msg>copy network manager a bit more directly<commit_after># Copyright 2015 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

""" Network Model

Provides network device listings and extended network information

"""

import logging
import textwrap

from urwid import (
    connect_signal,
    LineBox,
    ProgressBar,
    RadioButton,
    Text,
    )
from urwid import Padding as uPadding

from subiquitycore.ui.actionmenu import ActionMenu
from subiquitycore.ui.buttons import back_btn, cancel_btn, done_btn, menu_btn
from subiquitycore.ui.container import (
    Columns,
    ListBox,
    Pile,
    WidgetWrap,
    )
from subiquitycore.ui.form import Form, StringField, Toggleable, ChoiceField
from subiquitycore.ui.stretchy import Stretchy
from subiquitycore.ui.table import ColSpec, TablePile, TableRow
from subiquitycore.ui.utils import button_pile, Color, make_action_menu_row, Padding
from subiquitycore.view import BaseView


log = logging.getLogger('subiquitycore.views.network')


class ApplyingConfigWidget(WidgetWrap):

    def __init__(self, step_count, cancel_func):
        self.cancel_func = cancel_func
        button = cancel_btn(_("Cancel"), on_press=self.do_cancel)
        self.bar = ProgressBar(normal='progress_incomplete',
                               complete='progress_complete',
                               current=0, done=step_count)
        box = LineBox(Pile([self.bar,
                            button_pile([button])]),
                      title=_("Applying network config"))
        super().__init__(box)

    def advance(self):
        self.bar.current += 1

    def do_cancel(self, sender):
        self.cancel_func()


def _build_wifi_info(dev):
    r = []
    if dev.actual_ssid is not None:
        if dev.configured_ssid is not None:
            if dev.actual_ssid != dev.configured_ssid:
                r.append(
                    Text(_("Associated to '%s', will "
                           "associate to '%s'" % (dev.actual_ssid,
                                                  dev.configured_ssid))))
            else:
                r.append(Text(_("Associated to '%s'" % dev.actual_ssid)))
        else:
            r.append(Text(_("No access point configured, but associated "
                            "to '%s'" % dev.actual_ssid)))
    else:
        if dev.configured_ssid is not None:
            r.append(Text(_("Will associate to '%s'" % dev.configured_ssid)))
        else:
            r.append(Text(_("No access point configured")))
    return r


def _format_address_list(label, addresses):
    if len(addresses) == 0:
        return []
    elif len(addresses) == 1:
        return [Text(label % ('',) + ' ' + str(addresses[0]))]
    else:
        ips = []
        for ip in addresses:
            ips.append(str(ip))
        return [Text(label % ('es',) + ' ' + ', '.join(ips))]


def _build_gateway_ip_info_for_version(dev, version):
    actual_ip_addresses = dev.actual_ip_addresses_for_version(version)
    configured_ip_addresses = dev.configured_ip_addresses_for_version(version)
    if dev.dhcp_for_version(version):
        if dev.actual_ip_addresses_for_version(version):
            return _format_address_list(_("Will use DHCP for IPv%s, currently "
                                          "has address%%s:" % version),
                                        actual_ip_addresses)
        return [Text(_("Will use DHCP for IPv%s" % version))]
    elif configured_ip_addresses:
        if sorted(actual_ip_addresses) == sorted(configured_ip_addresses):
            return _format_address_list(
                _("Using static address%%s for IPv%s:" % version),
                actual_ip_addresses)
        p = _format_address_list(
            _("Will use static address%%s for IPv%s:" % version),
            configured_ip_addresses)
        if actual_ip_addresses:
            p.extend(_format_address_list(_("Currently has address%s:"),
                                          actual_ip_addresses))
        return p
    elif actual_ip_addresses:
        return _format_address_list(_("Has no IPv%s configuration, currently "
                                      "has address%%s:" % version),
                                    actual_ip_addresses)
    else:
        return [Text(_("IPv%s is not configured" % version))]


from .network_configure_manual_interface import NetworkConfigForm

network_choices = [
    (_("Automatic (DHCP)"), True, "dhcp"),
    (_("Manual"), True, "manual"),
    (_("Disable"), True, "disable"),
    ]

class NetworkMethodForm(Form):
    method = ChoiceField("IPv{ip_version} Method: ", choices=network_choices)

class EditNetworkStretchy(Stretchy):

    def __init__(self, parent, device, ip_version):
        self.parent = parent
        self.device = device
        self.ip_version = ip_version

        self.method_form = NetworkMethodForm()
        self.method_form.method.caption = _("IPv{ip_version} Method: ").format(ip_version=ip_version)
        connect_signal(self.method_form.method.widget, 'select', self._select_method)

        self.manual_form = NetworkConfigForm(ip_version)

        connect_signal(self.method_form, 'submit', self.done)
        connect_signal(self.method_form, 'cancel', self.cancel)

        self.form_pile = Pile(self.method_form.as_rows())

        widgets = [self.form_pile, Text(""), self.method_form.buttons]
        super().__init__(
            "Edit {device} IPv{ip_version} configuration".format(device=device.name, ip_version=ip_version),
            widgets,
            0, 0)

    def _select_method(self, sender, method):
        rows = []
        def r(w):
            rows.append((w, self.form_pile.options('pack')))
        for row in self.method_form.as_rows():
            r(row)
        if method == 'manual':
            r(Text(""))
            for row in self.manual_form.as_rows():
                r(row)
        self.form_pile.contents[:] = rows

    def done(self, sender):
        # XXX this converting from and to and from strings thing is a
        # bit out of hand.
        gateway = self.form.gateway.value
        if gateway is not None:
            gateway = str(gateway)
        result = {
            'network': str(self.form.subnet.value),
            'address': str(self.form.address.value),
            'gateway': gateway,
            'nameservers': list(map(str, self.form.nameservers.value)),
            'searchdomains': self.form.searchdomains.value,
        }
        self.dev.remove_ip_networks_for_version(self.ip_version)
        self.dev.remove_nameservers()
        self.dev.add_network(self.ip_version, result)

        self.parent.refresh_model_inputs()
        self.parent.remove_overlay()

    def cancel(self, sender=None):
        self.parent.remove_overlay()


class NetworkView(BaseView):
    title = _("Network connections")
    excerpt = _("Configure at least one interface this server can use to talk "
                "to other machines, and which preferably provides sufficient "
{'fraction_non_alphanumeric': 0.09865767374198241, 'fraction_numerical': 0.0022482311710639424, 'mean_word_length': 2.4017094017094016, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3498150', 'n_tokens_mistral': 4353, 'n_tokens_neox': 4193, 'n_words': 1093}