Ejemplo n.º 1
0
def test_apply_networking(switch, network, fresh_database):
    '''Test to validate apply_networking commits actions incrementally

    This test verifies that the apply_networking() function in hil/deferred.py
    incrementally commits actions, which ensures that any error on an action
    will not require a complete rerun of the prior actions (e.g. if an error
    is thrown on the 3rd action, the 1st and 2nd action will have already been
    committed)

    The test also verifies that if a new networking action fails, then the
    old networking actions in the queue were commited.
    '''
    nic = []
    actions = []
    # initialize 3 nics and networking actions
    for i in range(0, 3):
        interface = 'gi1/0/%d' % (i)
        nic.append(new_nic(str(i)))
        nic[i].port = model.Port(label=interface, switch=switch)
        actions.append(
            model.NetworkingAction(nic=nic[i],
                                   new_network=network,
                                   channel='vlan/native',
                                   type='modify_port'))

    # this makes the last action invalid for the test switch because the switch
    # raises an error when the networking action is of type revert port.
    actions[2] = model.NetworkingAction(nic=nic[2],
                                        new_network=None,
                                        channel='',
                                        type='revert_port')
    for action in actions:
        db.session.add(action)
    db.session.commit()

    with pytest.raises(RevertPortError):
        deferred.apply_networking()

    # close the session opened by `apply_networking` when `handle_actions`
    # fails; without this the tests would just stall (when using postgres)
    db.session.close()

    local_db = new_db()

    pending_action = local_db.session \
        .query(model.NetworkingAction) \
        .order_by(model.NetworkingAction.id).one_or_none()
    current_count = local_db.session \
        .query(model.NetworkingAction).count()

    local_db.session.delete(pending_action)
    local_db.session.commit()
    local_db.session.close()

    # test that there's only pending action in the queue and it is of type
    # revert_port
    assert current_count == 1
    assert pending_action.type == 'revert_port'
Ejemplo n.º 2
0
Archivo: api.py Proyecto: djfinn14/hil
def node_detach_network(node, nic, network):
    """Detach network ``network`` from physical nic ``nic``.

    Raises ProjectMismatchError if the node is not in a project.

    Raises BlockedError if there is already a pending network action.

    Raises BadArgumentError if the network is not attached to the nic.
    """
    auth_backend = get_auth_backend()

    node = _must_find(model.Node, node)
    network = _must_find(model.Network, network)
    nic = _must_find_n(node, model.Nic, nic)

    if not node.project:
        raise errors.ProjectMismatchError("Node not in project")
    auth_backend.require_project_access(node.project)

    if nic.current_action:
        raise errors.BlockedError(
            "A networking operation is already active on the nic.")
    attachment = model.NetworkAttachment.query \
        .filter_by(nic=nic, network=network).first()
    if attachment is None:
        raise errors.BadArgumentError(
            "The network is not attached to the nic.")
    db.session.add(
        model.NetworkingAction(type='modify_port',
                               nic=nic,
                               channel=attachment.channel,
                               new_network=None))
    db.session.commit()
    return '', 202
Ejemplo n.º 3
0
Archivo: api.py Proyecto: djfinn14/hil
def port_revert(switch, port):
    get_auth_backend().require_admin()
    switch = _must_find(model.Switch, switch)
    port = _must_find_n(switch, model.Port, port)

    if port.nic is None:
        raise errors.NotFoundError(port.label + " not attached")
    if port.nic.current_action:
        raise errors.BlockedError("Port already has a pending action.")

    db.session.add(
        model.NetworkingAction(type='revert_port',
                               nic=port.nic,
                               channel='',
                               new_network=None))
    db.session.commit()
Ejemplo n.º 4
0
def node_detach_network(node, nic, network):
    """Detach network ``network`` from physical nic ``nic``.

    Raises ProjectMismatchError if the node is not in a project.

    Raises BlockedError if there is already a pending network action.

    Raises BadArgumentError if the network is not attached to the nic.
    """
    auth_backend = get_auth_backend()

    node = get_or_404(model.Node, node)
    network = get_or_404(model.Network, network)
    nic = get_child_or_404(node, model.Nic, nic)

    if not node.project:
        raise errors.ProjectMismatchError("Node not in project")
    auth_backend.require_project_access(node.project)

    check_pending_action(nic)

    attachment = model.NetworkAttachment.query \
        .filter_by(nic=nic, network=network).one_or_none()
    if attachment is None:
        raise errors.BadArgumentError(
            "The network is not attached to the nic.")

    switch = nic.port.owner
    switch.ensure_legal_operation(nic, 'detach', attachment.channel)

    unique_id = str(uuid.uuid4())
    db.session.add(
        model.NetworkingAction(type='modify_port',
                               nic=nic,
                               channel=attachment.channel,
                               uuid=unique_id,
                               status='PENDING',
                               new_network=None))

    db.session.commit()
    return json.dumps({'status_id': unique_id}), 202
Ejemplo n.º 5
0
def port_revert(switch, port):
    """Detach the port from all networks."""
    get_auth_backend().require_admin()
    switch = get_or_404(model.Switch, switch)
    port = get_child_or_404(switch, model.Port, port)

    if port.nic is None:
        raise errors.NotFoundError(port.label + " not attached")
    check_pending_action(port.nic)
    unique_id = str(uuid.uuid4())

    action = model.NetworkingAction(type='revert_port',
                                    nic=port.nic,
                                    channel='',
                                    uuid=unique_id,
                                    status='PENDING',
                                    new_network=None)

    db.session.add(action)
    db.session.commit()
    return json.dumps({'status_id': unique_id})
Ejemplo n.º 6
0
    def test_modify_port(self, switch, nic, network):
        """Test the modify_port method"""
        # Create a port on the switch and connect it to the nic
        port = model.Port(label=INTERFACE1, switch=switch)
        port.nic = nic

        # Test action to set a network as native
        action_native = model.NetworkingAction(type='modify_port',
                                               nic=nic,
                                               new_network=network,
                                               channel='vlan/native')

        with requests_mock.mock() as mock:
            url_switch = switch._construct_url(INTERFACE1)
            mock.post(url_switch)
            url_mode = switch._construct_url(INTERFACE1, suffix='mode')
            mock.put(url_mode)
            url_tag = switch._construct_url(INTERFACE1,
                                            suffix='trunk/tag/native-vlan')
            mock.delete(url_tag)
            url_trunk = switch._construct_url(INTERFACE1, suffix='trunk')
            mock.put(url_trunk)

            switch.modify_port(action_native.nic.port.label,
                               action_native.channel,
                               action_native.new_network.network_id)

            assert mock.called
            assert mock.call_count == 4
            assert mock.request_history[0].text == SWITCHPORT_PAYLOAD
            assert mock.request_history[1].text == TRUNK_PAYLOAD
            assert mock.request_history[3].text == TRUNK_NATIVE_PAYLOAD

        # Test action to remove a native network
        action_rm_native = model.NetworkingAction(type='modify_port',
                                                  nic=nic,
                                                  new_network=None,
                                                  channel='vlan/native')

        with requests_mock.mock() as mock:
            url_native = switch._construct_url(INTERFACE1,
                                               suffix='trunk/native-vlan')
            mock.delete(url_native)

            switch.modify_port(action_rm_native.nic.port.label,
                               action_rm_native.channel, None)

            assert mock.called
            assert mock.call_count == 1

        # Test action to add a vlan
        action_vlan = model.NetworkingAction(type='modify_port',
                                             nic=nic,
                                             new_network=network,
                                             channel='vlan/102')
        with requests_mock.mock() as mock:
            url_switch = switch._construct_url(INTERFACE1)
            mock.post(url_switch)
            url_mode = switch._construct_url(INTERFACE1, suffix='mode')
            mock.put(url_mode)
            url_trunk = switch._construct_url(INTERFACE1,
                                              suffix='trunk/allowed/vlan')
            mock.put(url_trunk)

            switch.modify_port(action_vlan.nic.port.label, action_vlan.channel,
                               action_vlan.new_network.network_id)

            assert mock.called
            assert mock.call_count == 3
            assert mock.request_history[0].text == SWITCHPORT_PAYLOAD
            assert mock.request_history[1].text == TRUNK_PAYLOAD
            assert mock.request_history[2].text == TRUNK_VLAN_PAYLOAD

        # Test action to remove a vlan
        action_rm_vlan = model.NetworkingAction(type='modify_port',
                                                nic=nic,
                                                new_network=None,
                                                channel='vlan/102')
        with requests_mock.mock() as mock:
            url_trunk = switch._construct_url(INTERFACE1,
                                              suffix='trunk/allowed/vlan')
            mock.put(url_trunk)

            switch.modify_port(action_rm_vlan.nic.port.label,
                               action_rm_vlan.channel, None)

            assert mock.called
            assert mock.call_count == 1
            assert mock.request_history[0].text == TRUNK_REMOVE_VLAN_PAYLOAD
Ejemplo n.º 7
0
def node_connect_network(node, nic, network, channel=None):
    """Connect a physical NIC to a network, on channel.

    If channel is ``None``, use the allocator default.

    Raises ProjectMismatchError if the node is not in a project, or if the
    project does not have access rights to the given network.

    Raises BlockedError if there is a pending network action, or if the network
    is already attached to the nic, or if the channel is in use.

    Raises BadArgumentError if the channel is invalid for the network.
    """
    def _have_attachment(nic, query):
        """Return whether there are any attachments matching ``query`` for ``nic``.

        ``query`` should an argument suitable to pass to db.query(...).filter
        """
        return model.NetworkAttachment.query.filter(
            model.NetworkAttachment.nic == nic,
            query,
        ).first() is not None

    auth_backend = get_auth_backend()

    node = get_or_404(model.Node, node)
    nic = get_child_or_404(node, model.Nic, nic)
    network = get_or_404(model.Network, network)

    if not node.project:
        raise errors.ProjectMismatchError("Node not in project")
    auth_backend.require_project_access(node.project)

    project = node.project

    allocator = get_network_allocator()

    if nic.port is None:
        raise errors.NotFoundError("No port is connected to given nic.")

    check_pending_action(nic)

    if (network.access) and (project not in network.access):
        raise errors.ProjectMismatchError(
            "Project does not have access to given network.")

    if _have_attachment(nic, model.NetworkAttachment.network == network):
        raise errors.BlockedError(
            "The network is already attached to the nic.")

    if channel is None:
        channel = allocator.get_default_channel()

    if _have_attachment(nic, model.NetworkAttachment.channel == channel):
        raise errors.BlockedError("The channel is already in use on the nic.")

    if not allocator.is_legal_channel_for(channel, network.network_id):
        raise errors.BadArgumentError(
            "Channel %r, is not legal for this network." % channel)

    switch = nic.port.owner
    switch.ensure_legal_operation(nic, 'connect', channel)

    unique_id = str(uuid.uuid4())
    db.session.add(
        model.NetworkingAction(type='modify_port',
                               nic=nic,
                               new_network=network,
                               channel=channel,
                               uuid=unique_id,
                               status='PENDING'))
    db.session.commit()
    return json.dumps({'status_id': unique_id}), 202
Ejemplo n.º 8
0
def test_apply_networking(switch, network, fresh_database):
    '''Test to validate apply_networking commits actions incrementally

    This test verifies that the apply_networking() function in hil/deferred.py
    incrementally commits actions, which ensures that any error on an action
    will not require a complete rerun of the prior actions (e.g. if an error
    is thrown on the 3rd action, the 1st and 2nd action will have already been
    committed)

    The test also verifies that if a new networking action fails, then the
    old networking actions in the queue were commited.
    '''
    nic = []
    actions = []
    # initialize 3 nics and networking actions
    for i in range(0, 2):
        interface = 'gi1/0/%d' % (i)
        nic.append(new_nic(str(i)))
        nic[i].port = model.Port(label=interface, switch=switch)
        unique_id = str(uuid.uuid4())
        actions.append(
            model.NetworkingAction(nic=nic[i],
                                   new_network=network,
                                   channel='vlan/native',
                                   type='modify_port',
                                   uuid=unique_id,
                                   status='PENDING'))

    # Create another aciton of type revert_port. This action is invalid for the
    # test switch because the switch raises an error when the networking action
    # is of type revert port.
    unique_id = str(uuid.uuid4())
    nic.append(new_nic('2'))
    nic[2].port = model.Port(label=interface, switch=switch)
    actions.append(
        model.NetworkingAction(nic=nic[2],
                               new_network=None,
                               uuid=unique_id,
                               channel='',
                               status='PENDING',
                               type='revert_port'))

    # get some nic attributes before we close this db session.
    nic2_label = nic[2].label
    nic2_node = nic[2].owner.label

    for action in actions:
        db.session.add(action)
    db.session.commit()

    # simple check to ensure that right number of actions are added.
    total_count = db.session.query(model.NetworkingAction).count()
    assert total_count == 3

    deferred.apply_networking()

    # close the session opened by `apply_networking` when `handle_actions`
    # fails; without this the tests would just stall (when using postgres)
    db.session.close()

    local_db = new_db()

    errored_action = local_db.session \
        .query(model.NetworkingAction) \
        .order_by(model.NetworkingAction.id).filter_by(status='ERROR') \
        .one_or_none()

    # Count the number of actions with different statuses
    error_count = local_db.session \
        .query(model.NetworkingAction).filter_by(status='ERROR').count()

    pending_count = local_db.session \
        .query(model.NetworkingAction).filter_by(status='PENDING').count()

    done_count = local_db.session \
        .query(model.NetworkingAction).filter_by(status='DONE').count()

    # test that there's only 1 action that errored out in the queue and that it
    # is of type revert_port
    assert error_count == 1
    assert errored_action.type == 'revert_port'

    assert pending_count == 0
    assert done_count == 2
    local_db.session.commit()
    local_db.session.close()

    # add another action on a nic with a previously failed action.
    api.network_create('corsair', 'admin', '', '105')
    api.node_connect_network(nic2_node, nic2_label, 'corsair')

    # the api call should delete the errored action on that nic, and a new
    # pending action should appear.
    local_db = new_db()
    errored_action = local_db.session \
        .query(model.NetworkingAction) \
        .order_by(model.NetworkingAction.id).filter_by(status='ERROR') \
        .one_or_none()

    pending_action = local_db.session \
        .query(model.NetworkingAction) \
        .order_by(model.NetworkingAction.id).filter_by(status='PENDING') \
        .one_or_none()

    assert errored_action is None
    assert pending_action is not None

    local_db.session.commit()
    local_db.session.close()
Ejemplo n.º 9
0
def create_bigint_db():
    """Create database objects used in 'after-PK-bigint.sql'"""
    from hil.ext.switches.n3000 import DellN3000
    from hil.ext.switches.dell import PowerConnect55xx
    from hil.ext.switches.brocade import Brocade
    from hil.ext.switches.nexus import Nexus
    from hil.ext.switches.mock import MockSwitch
    from hil.ext.obm.ipmi import Ipmi
    from hil.ext.obm.mock import MockObm
    from hil.ext.auth.database import User
    from hil.ext.auth import database as dbauth
    with app.app_context():
        db.session.add(
            DellN3000(label='sw-n3000',
                      hostname='host',
                      username='******',
                      password='******',
                      dummy_vlan='5',
                      type=DellN3000.api_name))
        dell1 = PowerConnect55xx(label='sw-dell',
                                 hostname='host',
                                 username='******',
                                 password='******',
                                 type=PowerConnect55xx.api_name)
        db.session.add(dell1)
        db.session.add(
            Nexus(label='sw-nexus',
                  hostname='host',
                  username='******',
                  password='******',
                  dummy_vlan='5',
                  type=Nexus.api_name))
        db.session.add(
            Brocade(label='sw-brocade',
                    hostname='host',
                    username='******',
                    password='******',
                    interface_type='4',
                    type=Brocade.api_name))
        db.session.add(
            MockSwitch(label='sw0',
                       hostname='host',
                       username='******',
                       password='******',
                       type=MockSwitch.api_name))
        proj = model.Project(label='runway')
        db.session.add(proj)
        headnode1 = model.Headnode(label='runway_headnode',
                                   project=proj,
                                   base_img='image1')
        db.session.add(headnode1)
        db.session.add(model.Hnic(label='hnic1', headnode=headnode1))
        ipmi = Ipmi(host='host', user='******', password='******')
        db.session.add(ipmi)
        mock_obm = MockObm(host='host', user='******', password='******')
        db.session.add(mock_obm)
        node1 = model.Node(label='node-1', obm=ipmi)
        db.session.add(node1)

        db.session.add(
            model.Metadata(label='meta',
                           value="it is a true value",
                           node=node1))
        network1 = model.Network(owner=None,
                                 access=[proj],
                                 allocated=False,
                                 network_id="networking network",
                                 label='hil wireless')
        db.session.add(network1)
        nic1 = model.Nic(node=node1, label='pxe', mac_addr='ff:ff:ff:ff:ff:fe')
        model.Port(label='A fine port', switch=dell1)
        db.session.add(nic1)
        db.session.add(
            model.NetworkAttachment(nic=nic1, network_id=1,
                                    channel='vlan/100'))
        db.session.add(
            model.NetworkingAction(type='modify_port',
                                   nic=nic1,
                                   new_network=network1,
                                   channel='vlan/100'))
        jim = User(label='jim', password='******', is_admin=True)
        db.session.add(jim)

        local.auth = dbauth.User.query.filter_by(label='jim').one()

        dbauth.user_add_project('jim', 'runway')

        db.session.commit()

        # Original password is "pass"
        db.session.query(User).get(1).hashed_password = \
            ('$6$rounds=656000$iTyrApYTUhMx4b4g$YcaMExV'
             'YtS0ut2yXWrT64OggFpE4lLg12QsAuyMA3YKX6Czth'
             'XeisA47dJZW9GwU2q2CTIVrsbpxAVT64Pih2/')
        db.session.commit()
Ejemplo n.º 10
0
Archivo: api.py Proyecto: djfinn14/hil
def node_connect_network(node, nic, network, channel=None):
    """Connect a physical NIC to a network, on channel.

    If channel is ``None``, use the allocator default.

    Raises ProjectMismatchError if the node is not in a project, or if the
    project does not have access rights to the given network.

    Raises BlockedError if there is a pending network action, or if the network
    is already attached to the nic, or if the channel is in use.

    Raises BadArgumentError if the channel is invalid for the network.
    """
    def _have_attachment(nic, query):
        """Return whether there are any attachments matching ``query`` for ``nic``.

        ``query`` should an argument suitable to pass to db.query(...).filter
        """
        return model.NetworkAttachment.query.filter(
            model.NetworkAttachment.nic == nic,
            query,
        ).count() != 0

    auth_backend = get_auth_backend()

    node = _must_find(model.Node, node)
    nic = _must_find_n(node, model.Nic, nic)
    network = _must_find(model.Network, network)

    if not node.project:
        raise errors.ProjectMismatchError("Node not in project")
    auth_backend.require_project_access(node.project)

    project = node.project

    allocator = get_network_allocator()

    if nic.port is None:
        raise errors.NotFoundError("No port is connected to given nic.")

    if nic.current_action:
        raise errors.BlockedError(
            "A networking operation is already active on the nic.")

    if (network.access) and (project not in network.access):
        raise errors.ProjectMismatchError(
            "Project does not have access to given network.")

    if _have_attachment(nic, model.NetworkAttachment.network == network):
        raise errors.BlockedError(
            "The network is already attached to the nic.")

    if channel is None:
        channel = allocator.get_default_channel()

    if _have_attachment(nic, model.NetworkAttachment.channel == channel):
        raise errors.BlockedError("The channel is already in use on the nic.")

    if not allocator.is_legal_channel_for(channel, network.network_id):
        raise errors.BadArgumentError(
            "Channel %r, is not legal for this network." % channel)

    db.session.add(
        model.NetworkingAction(type='modify_port',
                               nic=nic,
                               new_network=network,
                               channel=channel))
    db.session.commit()
    return '', 202