コード例 #1
0
ファイル: twisted.py プロジェクト: marcoceppi/nsone-python
 def _onBody(self, body, response, user_callback, data, headers):
     self._logHeaders(headers)
     self._log.debug("%s %s %s %s" % (response.request.method,
                                      response.request.absoluteURI,
                                      response.code,
                                      data))
     if response.code != 200:
         if response.code == 429:
             raise RateLimitException('rate limit exceeded',
                                      response,
                                      body)
         elif response.code == 401:
             raise AuthException('unauthorized',
                                 response,
                                 body)
         else:
             raise ResourceException('server error', response, body)
     try:
         jsonOut = json.loads(body)
     except:
         raise ResourceException('invalid json in response',
                                 response,
                                 body)
     if user_callback:
         # set these in case callback throws, so we have them for errback
         self.response = response
         self.body = body
         return user_callback(jsonOut, body, response)
     else:
         return jsonOut
コード例 #2
0
ファイル: twisted.py プロジェクト: quakehead/nsone-python
 def _errback(self, failure, user_errback):
     # print "failure: %s" % failure.printTraceback()
     if user_errback:
         return user_errback(failure)
     else:
         if failure.check(ResourceException, RateLimitException):
             raise failure.value
         raise ResourceException(failure.getErrorMessage())
コード例 #3
0
    def test_populate(self, load_mock):
        provider = Ns1Provider('test', 'api-key')

        # Bad auth
        load_mock.side_effect = AuthException('unauthorized')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(AuthException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # General error
        load_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(ResourceException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])

        # Non-existant zone doesn't populate anything
        load_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])

        # Existing zone w/o records
        load_mock.reset_mock()
        nsone_zone = DummyZone([])
        load_mock.side_effect = [nsone_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])

        # Existing zone w/records
        load_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records)
        load_mock.side_effect = [nsone_zone]
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(self.expected, zone.records)
        self.assertEquals(('unit.tests', ), load_mock.call_args[0])
コード例 #4
0
        def handleProblem(code, resp, msg):
            if errback:
                errback((resp, msg))
                return

            if code == 429:
                raise RateLimitException('rate limit exceeded', resp, msg)
            elif code == 401:
                raise AuthException('unauthorized', resp, msg)
            else:
                raise ResourceException('server error', resp, msg)
コード例 #5
0
    def send(self,
             method,
             url,
             headers=None,
             data=None,
             files=None,
             callback=None,
             errback=None):
        if files is not None:
            # XXX
            raise Exception('file uploads not supported in BasicTransport yet')
        self._logHeaders(headers)
        self._log.debug("%s %s %s" % (method, url, data))
        opener = build_opener(HTTPSHandler)
        request = Request(url, headers=headers, data=data)
        request.get_method = lambda: method

        def handleProblem(code, resp, msg):
            if errback:
                errback((resp, msg))
                return

            if code == 429:
                raise RateLimitException('rate limit exceeded', resp, msg)
            elif code == 401:
                raise AuthException('unauthorized', resp, msg)
            else:
                raise ResourceException('server error', resp, msg)

        # Handle error and responses the same so we can
        # always pass the body to the handleProblem function
        try:
            resp = opener.open(request)
        except HTTPError as e:
            resp = e
        finally:
            body = resp.read()
            if resp.code != 200:
                handleProblem(resp.code, resp, body)

        # TODO make sure json is valid
        try:
            jsonOut = json.loads(body)
        except ValueError:
            if errback:
                errback(resp)
                return
            else:
                raise ResourceException('invalid json in response', resp,
                                        resp.text)
        if callback:
            return callback(jsonOut)
        else:
            return jsonOut
コード例 #6
0
ファイル: requests.py プロジェクト: tomprince/nsone-python
 def send(self,
          method,
          url,
          headers=None,
          data=None,
          files=None,
          callback=None,
          errback=None):
     self._logHeaders(headers)
     resp = self.REQ_MAP[method](url,
                                 headers=headers,
                                 verify=self._verify,
                                 data=data,
                                 files=files)
     if resp.status_code != 200:
         if errback:
             errback(resp)
             return
         else:
             if resp.status_code == 429:
                 raise RateLimitException('rate limit exceeded', resp,
                                          resp.text)
             elif resp.status_code == 401:
                 raise AuthException('unauthorized', resp, resp.text)
             else:
                 raise ResourceException('server error', resp, resp.text)
     # TODO make sure json is valid
     try:
         jsonOut = resp.json()
     except ValueError:
         if errback:
             errback(resp)
             return
         else:
             raise ResourceException('invalid json in response', resp,
                                     resp.text)
     if callback:
         return callback(jsonOut)
     else:
         return jsonOut
コード例 #7
0
ファイル: resource.py プロジェクト: tomprince/nsone-python
    def __init__(self, config):
        """

        :param nsone.config.Config config: config object used to build requests
        """
        self._config = config
        self._log = logging.getLogger(__name__)
        # TODO verify we have a default key
        # get a transport. TODO make this static property?
        transport = self._config.get('transport', None)
        if transport is None:
            # for default transport:
            # if requests is available, use that. otherwise, basic
            from nsone.rest.transport.requests import have_requests
            if have_requests:
                transport = 'requests'
            else:
                transport = 'basic'
        if transport not in TransportBase.REGISTRY:
            raise ResourceException('requested transport was not found: %s'
                                    % transport)
        self._transport = TransportBase.REGISTRY[transport](self._config)
コード例 #8
0
    def test_sync(self, load_mock, create_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        desired.records.update(self.expected)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))

        # Fails, general error
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # Fails, bad auth
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(create_mock.side_effect, ctx.exception)

        # non-existant zone, create
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = None
        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Update & delete
        load_mock.reset_mock()
        create_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records +
                               [{
                                   'type': 'A',
                                   'ttl': 42,
                                   'short_answers': ['9.9.9.9'],
                                   'domain': 'delete-me.unit.tests.',
                               }])
        nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
        nsone_zone.loadRecord = Mock()
        load_mock.side_effect = [nsone_zone, nsone_zone]
        plan = provider.plan(desired)
        self.assertEquals(2, len(plan.changes))
        self.assertIsInstance(plan.changes[0], Update)
        self.assertIsInstance(plan.changes[1], Delete)

        got_n = provider.apply(plan)
        self.assertEquals(2, got_n)
        nsone_zone.loadRecord.assert_has_calls([
            call('unit.tests', u'A'),
            call().update(answers=[u'1.2.3.4'], ttl=32),
            call('delete-me', u'A'),
            call().delete()
        ])
コード例 #9
0
    def test_sync(self, load_mock, create_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        for r in self.expected:
            desired.add_record(r)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))

        # Fails, general error
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # Fails, bad auth
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(create_mock.side_effect, ctx.exception)

        # non-existant zone, create
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        # ugh, need a mock zone with a mock prop since we're using getattr, we
        # can actually control side effects on `meth` with that.
        mock_zone = Mock()
        mock_zone.add_SRV = Mock()
        mock_zone.add_SRV.side_effect = [
            RateLimitException('boo', period=0),
            None,
        ]
        create_mock.side_effect = [mock_zone]
        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Update & delete
        load_mock.reset_mock()
        create_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records + [{
            'type': 'A',
            'ttl': 42,
            'short_answers': ['9.9.9.9'],
            'domain': 'delete-me.unit.tests.',
        }])
        nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
        nsone_zone.loadRecord = Mock()
        zone_search = Mock()
        zone_search.return_value = [
            {
                "domain": "geo.unit.tests",
                "zone": "unit.tests",
                "type": "A",
                "answers": [
                    {'answer': ['1.1.1.1'], 'meta': {}},
                    {'answer': ['1.2.3.4'],
                     'meta': {'ca_province': ['ON']}},
                    {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
                    {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
                    {'answer': ['4.5.6.7'],
                     'meta': {'iso_region_code': ['NA-US-WA']}},
                ],
                'ttl': 34,
            },
        ]
        nsone_zone.search = zone_search
        load_mock.side_effect = [nsone_zone, nsone_zone]
        plan = provider.plan(desired)
        self.assertEquals(3, len(plan.changes))
        self.assertIsInstance(plan.changes[0], Update)
        self.assertIsInstance(plan.changes[2], Delete)
        # ugh, we need a mock record that can be returned from loadRecord for
        # the update and delete targets, we can add our side effects to that to
        # trigger rate limit handling
        mock_record = Mock()
        mock_record.update.side_effect = [
            RateLimitException('one', period=0),
            None,
            None,
        ]
        mock_record.delete.side_effect = [
            RateLimitException('two', period=0),
            None,
            None,
        ]
        nsone_zone.loadRecord.side_effect = [mock_record, mock_record,
                                             mock_record]
        got_n = provider.apply(plan)
        self.assertEquals(3, got_n)
        nsone_zone.loadRecord.assert_has_calls([
            call('unit.tests', u'A'),
            call('geo', u'A'),
            call('delete-me', u'A'),
        ])
        mock_record.assert_has_calls([
            call.update(answers=[{'answer': [u'1.2.3.4'], 'meta': {}}],
                        filters=[],
                        ttl=32),
            call.update(answers=[{u'answer': [u'1.2.3.4'], u'meta': {}}],
                        filters=[],
                        ttl=32),
            call.update(
                answers=[
                    {u'answer': [u'101.102.103.104'], u'meta': {}},
                    {u'answer': [u'101.102.103.105'], u'meta': {}},
                    {
                        u'answer': [u'201.202.203.204'],
                        u'meta': {
                            u'iso_region_code': [u'NA-US-NY']
                        },
                    },
                ],
                filters=[
                    {u'filter': u'shuffle', u'config': {}},
                    {u'filter': u'geotarget_country', u'config': {}},
                    {u'filter': u'select_first_n', u'config': {u'N': 1}},
                ],
                ttl=34),
            call.delete(),
            call.delete()
        ])
コード例 #10
0
    def test_populate(self, load_mock):
        provider = Ns1Provider('test', 'api-key')

        # Bad auth
        load_mock.side_effect = AuthException('unauthorized')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(AuthException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # General error
        load_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        zone = Zone('unit.tests.', [])
        with self.assertRaises(ResourceException) as ctx:
            provider.populate(zone)
        self.assertEquals(load_mock.side_effect, ctx.exception)
        self.assertEquals(('unit.tests',), load_mock.call_args[0])

        # Non-existant zone doesn't populate anything
        load_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(set(), zone.records)
        self.assertEquals(('unit.tests',), load_mock.call_args[0])

        # Existing zone w/o records
        load_mock.reset_mock()
        nsone_zone = DummyZone([])
        load_mock.side_effect = [nsone_zone]
        zone_search = Mock()
        zone_search.return_value = [
            {
                "domain": "geo.unit.tests",
                "zone": "unit.tests",
                "type": "A",
                "answers": [
                    {'answer': ['1.1.1.1'], 'meta': {}},
                    {'answer': ['1.2.3.4'],
                     'meta': {'ca_province': ['ON']}},
                    {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
                    {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
                    {'answer': ['4.5.6.7'],
                     'meta': {'iso_region_code': ['NA-US-WA']}},
                ],
                'ttl': 34,
            },
        ]
        nsone_zone.search = zone_search
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(1, len(zone.records))
        self.assertEquals(('unit.tests',), load_mock.call_args[0])

        # Existing zone w/records
        load_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records)
        load_mock.side_effect = [nsone_zone]
        zone_search = Mock()
        zone_search.return_value = [
            {
                "domain": "geo.unit.tests",
                "zone": "unit.tests",
                "type": "A",
                "answers": [
                    {'answer': ['1.1.1.1'], 'meta': {}},
                    {'answer': ['1.2.3.4'],
                     'meta': {'ca_province': ['ON']}},
                    {'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
                    {'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
                    {'answer': ['4.5.6.7'],
                     'meta': {'iso_region_code': ['NA-US-WA']}},
                ],
                'ttl': 34,
            },
        ]
        nsone_zone.search = zone_search
        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(self.expected, zone.records)
        self.assertEquals(('unit.tests',), load_mock.call_args[0])
コード例 #11
0
    def test_sync(self, load_mock, create_mock):
        provider = Ns1Provider('test', 'api-key')

        desired = Zone('unit.tests.', [])
        for r in self.expected:
            desired.add_record(r)

        plan = provider.plan(desired)
        # everything except the root NS
        expected_n = len(self.expected) - 1
        self.assertEquals(expected_n, len(plan.changes))

        # Fails, general error
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = ResourceException('boom')
        with self.assertRaises(ResourceException) as ctx:
            provider.apply(plan)
        self.assertEquals(load_mock.side_effect, ctx.exception)

        # Fails, bad auth
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        create_mock.side_effect = AuthException('unauthorized')
        with self.assertRaises(AuthException) as ctx:
            provider.apply(plan)
        self.assertEquals(create_mock.side_effect, ctx.exception)

        # non-existant zone, create
        load_mock.reset_mock()
        create_mock.reset_mock()
        load_mock.side_effect = \
            ResourceException('server error: zone not found')
        # ugh, need a mock zone with a mock prop since we're using getattr, we
        # can actually control side effects on `meth` with that.
        mock_zone = Mock()
        mock_zone.add_SRV = Mock()
        mock_zone.add_SRV.side_effect = [
            RateLimitException('boo', period=0),
            None,
        ]
        create_mock.side_effect = [mock_zone]
        got_n = provider.apply(plan)
        self.assertEquals(expected_n, got_n)

        # Update & delete
        load_mock.reset_mock()
        create_mock.reset_mock()
        nsone_zone = DummyZone(self.nsone_records +
                               [{
                                   'type': 'A',
                                   'ttl': 42,
                                   'short_answers': ['9.9.9.9'],
                                   'domain': 'delete-me.unit.tests.',
                               }])
        nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
        nsone_zone.loadRecord = Mock()
        load_mock.side_effect = [nsone_zone, nsone_zone]
        plan = provider.plan(desired)
        self.assertEquals(2, len(plan.changes))
        self.assertIsInstance(plan.changes[0], Update)
        self.assertIsInstance(plan.changes[1], Delete)
        # ugh, we need a mock record that can be returned from loadRecord for
        # the update and delete targets, we can add our side effects to that to
        # trigger rate limit handling
        mock_record = Mock()
        mock_record.update.side_effect = [
            RateLimitException('one', period=0),
            None,
        ]
        mock_record.delete.side_effect = [
            RateLimitException('two', period=0),
            None,
        ]
        nsone_zone.loadRecord.side_effect = [mock_record, mock_record]
        got_n = provider.apply(plan)
        self.assertEquals(2, got_n)
        nsone_zone.loadRecord.assert_has_calls([
            call('unit.tests', u'A'),
            call('delete-me', u'A'),
        ])
        mock_record.assert_has_calls(
            [call.update(answers=[u'1.2.3.4'], ttl=32),
             call.delete()])